首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

sqlite3

2.5版本中的新功能。

SQLite是一个C库,它提供了一个轻量级的基于磁盘的数据库,它不需要单独的服务器进程,并允许使用SQL查询语言的非标准变体访问数据库。一些应用程序可以使用SQLite进行内部数据存储。也可以使用SQLite对应用程序进行原型设计,然后将代码移植到更大的数据库,如PostgreSQL或Oracle。

sqlite3模块由GerhardHäring编写。它提供了一个符合PEP 249描述的DB-API 2.0规范的SQL接口。

要使用该模块,您必须首先创建一个Connection代表数据库的对象。这里的数据将被存储在example.db文件中:

代码语言:javascript
复制
import sqlite3
conn = sqlite3.connect('example.db')

您还可以提供特殊名称:memory:以在RAM中创建数据库。

一旦你有了Connection,你可以创建一个Cursor对象并调用它的execute()方法来执行SQL命令:

代码语言:javascript
复制
c = conn.cursor()

# Create table
c.execute('''CREATE TABLE stocks
             (date text, trans text, symbol text, qty real, price real)''')

# Insert a row of data
c.execute("INSERT INTO stocks VALUES ('2006-01-05','BUY','RHAT',100,35.14)")

# Save (commit) the changes
conn.commit()

# We can also close the connection if we are done with it.
# Just be sure any changes have been committed or they will be lost.
conn.close()

您保存的数据是持久性数据,可在以后的会话中使用:

代码语言:javascript
复制
import sqlite3
conn = sqlite3.connect('example.db')
c = conn.cursor()

通常你的SQL操作需要使用Python变量的值。你不应该使用Python的字符串操作来组装你的查询,因为这样做是不安全的; 它会使您的程序容易受到SQL注入攻击(有关可能出错的幽默示例,请参阅https://xkcd.com/327/)。

而是使用DB-API的参数替换。将?作为占位符,无论你想使用的值,然后提供值的元组作为第二个参数光标的execute()方法。(其他数据库模块可能使用不同的占位符,例如%s:1)。例如:

代码语言:javascript
复制
# Never do this -- insecure!
symbol = 'RHAT'
c.execute("SELECT * FROM stocks WHERE symbol = '%s'" % symbol)

# Do this instead
t = ('RHAT',)
c.execute('SELECT * FROM stocks WHERE symbol=?', t)
print c.fetchone()

# Larger example that inserts many records at a time
purchases = [('2006-03-28', 'BUY', 'IBM', 1000, 45.00),
             ('2006-04-05', 'BUY', 'MSFT', 1000, 72.00),
             ('2006-04-06', 'SELL', 'IBM', 500, 53.00),
            ]
c.executemany('INSERT INTO stocks VALUES (?,?,?,?,?)', purchases)

要在执行SELECT语句后检索数据,可以将游标作为迭代器,调用游标的fetchone()方法来检索单个匹配的行,或者调用fetchall()获取匹配行的列表。

这个例子使用迭代器形式:

代码语言:javascript
复制
>>> for row in c.execute('SELECT * FROM stocks ORDER BY price'):
        print row

(u'2006-01-05', u'BUY', u'RHAT', 100, 35.14)
(u'2006-03-28', u'BUY', u'IBM', 1000, 45.0)
(u'2006-04-06', u'SELL', u'IBM', 500, 53.0)
(u'2006-04-05', u'BUY', u'MSFT', 1000, 72.0)

1.模块功能和常量

sqlite3.version

此模块的版本号,作为字符串。这不是SQLite库的版本。

sqlite3.version_info

该模块的版本号,作为整数的元组。这不是SQLite库的版本。

sqlite3.sqlite_version

运行时SQLite库的版本号,作为字符串。

sqlite3.sqlite_version_info

运行时SQLite库的版本号,作为整数的元组。

sqlite3.PARSE_DECLTYPES

该常数用于与函数的detect_types参数一起使用connect()

设置它会使sqlite3模块解析它返回的每一列的声明类型。它会解析出声明类型的第一个单词,即对于“整数主键”,它将解析出“整数”,或者对于“编号(10)”,它将解析出“编号”。然后,对于该列,它将查看转换器字典并使用在那里注册的转换器函数。

sqlite3.PARSE_COLNAMES

该常数用于与函数的detect_types参数一起使用connect()

设置这使得SQLite接口解析它返回的每一列的列名。它会在那里寻找一个形成mytype的字符串,然后决定'mytype'是该列的类型。它会尝试在转换器字典中找到'mytype'条目,然后使用在那里找到的转换器函数返回值。找到的列名Cursor.description只是列名的第一个单词,也就是说,如果您'as "x [datetime]"'在SQL中使用类似的名称,那么我们将解析出所有内容,直到列名称的第一个空格为止:列名称将简单地为“x”。

sqlite3.connect(database[, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements])

打开到SQLite数据库文件数据库的连接。您可以使用":memory:"打开数据库连接到驻留在RAM而不是磁盘上的数据库。

当数据库被多个连接访问时,其中一个进程修改了数据库,SQLite数据库被锁定,直到该事务被提交。该超时参数指定连接应该多长时间等待锁消失,直到引发异常。超时参数的默认值是5.0(五秒)。

有关isolation_level参数,请参阅对象的Connection.isolation_level属性Connection

SQLite本机仅支持TEXT,INTEGER,REAL,BLOB和NULL类型。如果你想使用其他类型,你必须自己添加对它们的支持。该detect_types参数和使用自定义转换器与模块级的注册register_converter()功能,让你轻松做到这一点。

detect_types默认为0(即关闭,没有类型检测),您可以将其设置为任意组合PARSE_DECLTYPESPARSE_COLNAMES打开类型检测。

默认情况下,sqlite3模块将其Connection类用于连接调用。但是,您可以继承这个Connection类,并connect()通过为工厂参数提供您的类来使用您的类。

有关详细信息,请参阅本手册的SQLite和Python类型部分。

sqlite3模块在内部使用一个语句缓存来避免SQL解析开销。如果要显式设置为连接缓存的语句数,可以设置cached_statements参数。目前实施的默认设置是缓存100条语句。

sqlite3.register_converter(typename, callable)

注册一个可调用的字符串,将数据库中的字符串转换为自定义的Python类型。可调用将为类型为typename的所有数据库值调用。赋予参数detect_types中的connect()该类型检测是如何工作的功能。请注意,查询中typename和类型的名称必须匹配!

sqlite3.register_adapter(type, callable)

注册可调用以将自定义Python类型类型转换为SQLite支持的类型之一。可调用callable接受Python值作为单个参数,并且必须返回以下类型的值:int,long,float,str(UTF-8编码),unicode或缓冲区。

sqlite3.complete_statement(sql)

返回True如果字符串SQL包含由分号终止一个或多个完整的SQL语句。它不验证SQL在语法上是否正确,只是没有未关闭的字符串文本,并且语句以分号结尾。

这可以用来为SQLite构建一个shell,如下例所示:

代码语言:javascript
复制
# A minimal SQLite shell for experiments

import sqlite3

con = sqlite3.connect(":memory:")
con.isolation_level = None
cur = con.cursor()

buffer = ""

print "Enter your SQL commands to execute in sqlite3."
print "Enter a blank line to exit."

while True:
    line = raw_input()
    if line == "":
        break
    buffer += line
    if sqlite3.complete_statement(buffer):
        try:
            buffer = buffer.strip()
            cur.execute(buffer)

            if buffer.lstrip().upper().startswith("SELECT"):
                print cur.fetchall()
        except sqlite3.Error as e:
            print "An error occurred:", e.args[0]
        buffer = ""

con.close()

sqlite3.enable_callback_tracebacks(flag)

默认情况下,你不会在用户定义的函数,聚合,转换器,授权者回调等中获得任何回溯。如果你想调试它们,你可以调用此标志设置为的函数True。之后,你会从回调中获得回溯sys.stderr。用于False再次禁用该功能。

2.连接对象

class sqlite3.Connection

SQLite数据库连接具有以下属性和方法:

isolation_level

获取或设置当前的隔离级别。None用于自动提交模式或“延迟”,“立即”或“独占”之一。有关更详细的解释,请参阅控制交易部分。

cursor(factory=Cursor)

游标方法接受一个可选的参数工厂。如果提供,它必须是可返回的实例Cursor或其子类。

commit()

此方法提交当前事务。如果您不调用此方法,则自从上次调用以来执行的任何操作commit()都无法从其他数据库连接看到。如果您想知道为什么您没有看到您写入数据库的数据,请检查您是否忘记调用此方法。

rollback()

此方法回滚自上次调用以来对数据库所做的任何更改commit()

close()

这将关闭数据库连接。请注意,这不会自动调用commit()。如果您只是在不commit()先调用的情况下关闭数据库连接,则更改将会丢失!

execute(sql[, parameters])

这是一个非标准的快捷方式,它通过调用游标方法创建中间游标对象,然后execute使用给定的参数调用游标的方法。

executemany(sql[, parameters])

这是一个非标准的快捷方式,它通过调用游标方法创建中间游标对象,然后executemany使用给定的参数调用游标的方法。

executescript(sql_script)

这是一个非标准的快捷方式,它通过调用游标方法创建中间游标对象,然后executescript使用给定的参数调用游标的方法。

create_function(name, num_params, func)

创建一个可以从SQL语句中以后使用下面的功能名称的用户定义函数的名称num_params是函数接受的参数的个数,func是一个可调用的Python,它被称为SQL函数。

该函数可以返回SQLite支持的任何类型:unicode,str,int,long,float,buffer和None

例:

代码语言:javascript
复制
import sqlite3
import md5

def md5sum(t):
    return md5.md5(t).hexdigest()

con = sqlite3.connect(":memory:")
con.create_function("md5", 1, md5sum)
cur = con.cursor()
cur.execute("select md5(?)", ("foo",))
print cur.fetchone()[0]

create_aggregate(name, num_params, aggregate_class)

创建用户定义的聚合函数。

聚合类必须实现一个step方法,该方法接受参数num_params的数量,以及一个finalize将返回聚合的最终结果的方法。

finalize方法可以返回SQLite支持的任何类型:unicode,str,int,long,float,buffer和None

例:

代码语言:javascript
复制
import sqlite3

class MySum:
    def __init__(self):
        self.count = 0

    def step(self, value):
        self.count += value

    def finalize(self):
        return self.count

con = sqlite3.connect(":memory:")
con.create_aggregate("mysum", 1, MySum)
cur = con.cursor()
cur.execute("create table test(i)")
cur.execute("insert into test(i) values (1)")
cur.execute("insert into test(i) values (2)")
cur.execute("select mysum(i) from test")
print cur.fetchone()[0]

create_collation(name, callable)

用指定名称创建一个排序规则并可调用。可调用将传递两个字符串参数。如果第一个的顺序低于第二个,则返回-1,如果顺序相等,则返回0;如果第一个的顺序高于第二个,则返回1。请注意,这会控制排序(SQL中的ORDER BY),因此您的比较不会影响其他SQL操作。

请注意,可调用函数将以Python字节串的形式获取其参数,通常以UTF-8编码。

以下示例显示了“错误方式”排序的自定义归类:

代码语言:javascript
复制
import sqlite3

def collate_reverse(string1, string2):
    return -cmp(string1, string2)

con = sqlite3.connect(":memory:")
con.create_collation("reverse", collate_reverse)

cur = con.cursor()
cur.execute("create table test(x)")
cur.executemany("insert into test(x) values (?)", [("a",), ("b",)])
cur.execute("select x from test order by x collate reverse")
for row in cur:
    print row
con.close()

要移除排序规则,请create_collation使用None可调用方式进行调用:

代码语言:javascript
复制
con.create_collation("reverse", None)

interrupt()

您可以从不同的线程调用此方法以中止可能在连接上执行的任何查询。该查询将中止,调用者将得到一个异常。

set_authorizer(authorizer_callback)

这个例程注册一个回调。每次尝试访问数据库中的一列表时,都会调用该回调。SQLITE_OK如果允许访问,SQLITE_DENY则应该返回回调,如果整个SQL语句应该中止并出现错误,SQLITE_IGNORE并且该列应被视为NULL值。这些常量在sqlite3模块中可用。

回调的第一个参数表示要授权哪种操作。第二个和第三个参数将是参数或None取决于第一个参数。第四个参数是数据库的名称(“main”,“temp”等)(如果适用)。第五个参数是负责访问尝试的最内部触发器或视图的名称,或者None如果此访问尝试直接来自输入SQL代码。

请参考SQLite文档,了解第一个参数的可能值以及第二个和第三个参数的含义,具体取决于第一个参数。sqlite3模块中提供了所有必需的常量。

set_progress_handler(handler, n)

这个例程注册一个回调。该回调函数针对SQLite虚拟机的每n个指令进行调用。如果您想在长时间运行期间从SQLite调用,例如更新GUI,这很有用。

如果要清除以前安装的任何进度处理程序,请使用Nonefor 处理程序调用该方法。

2.6版本中的新功能。

enable_load_extension(enabled)

此例程允许/禁止SQLite引擎从共享库加载SQLite扩展。SQLite扩展可以定义新的函数,聚合或全新的虚拟表实现。一个众所周知的扩展是与SQLite一起发布的全文搜索扩展。

可装载的扩展名默认是禁用的。见[1]。

2.7版本的新功能。

代码语言:javascript
复制
import sqlite3

con = sqlite3.connect(":memory:")

# enable extension loading
con.enable_load_extension(True)

# Load the fulltext search extension
con.execute("select load_extension('./fts3.so')")

# alternatively you can load the extension using an API call:
# con.load_extension("./fts3.so")

# disable extension laoding again
con.enable_load_extension(False)

# example from SQLite wiki
con.execute("create virtual table recipe using fts3(name, ingredients)")
con.executescript("""
    insert into recipe (name, ingredients) values ('broccoli stew', 'broccoli peppers cheese tomatoes');
    insert into recipe (name, ingredients) values ('pumpkin stew', 'pumpkin onions garlic celery');
    insert into recipe (name, ingredients) values ('broccoli pie', 'broccoli cheese onions flour');
    insert into recipe (name, ingredients) values ('pumpkin pie', 'pumpkin sugar flour butter');
    """)
for row in con.execute("select rowid, name, ingredients from recipe where name match 'pie'"):
    print row

load_extension(path)

此例程从共享库中加载SQLite扩展。您必须先启用扩展加载,enable_load_extension()然后才能使用此例程。

可装载的扩展名默认是禁用的。见[1]。

2.7版本的新功能。

row_factory

您可以将此属性更改为可接受的游标,将游标和原始行作为元组接受,并返回实际结果行。这样,您可以实现更高级的返回结果方式,例如返回也可以按名称访问列的对象。

例:

代码语言:javascript
复制
import sqlite3

def dict_factory(cursor, row):
    d = {}
    for idx, col in enumerate(cursor.description):
        d[col[0]] = row[idx]
    return d

con = sqlite3.connect(":memory:")
con.row_factory = dict_factory
cur = con.cursor()
cur.execute("select 1 as a")
print cur.fetchone()["a"]

如果返回一个元组不够,而你想要对列进行基于名称的访问,则应该考虑设置row_factory为高度优化的sqlite3.Row类型。Row提供基于索引和不区分大小写的基于名称的访问,几乎没有内存开销。它可能会比您自己的基于自定义字典的方法或甚至基于db_row的解决方案更好。

text_factory

使用此属性,您可以控制为TEXT数据类型返回哪些对象。默认情况下,此属性设置为unicodesqlite3模块将返回Unicode对象TEXT。如果您想要返回字节串,可以将其设置为str

出于效率原因,还有一种方法可以仅返回非ASCII数据的Unicode对象,否则返回字节串。要激活它,请将此属性设置为sqlite3.OptimizedUnicode

您还可以将其设置为接受单个字符串参数并返回结果对象的任何其他可调用对象。

有关说明,请参阅以下示例代码:

代码语言:javascript
复制
import sqlite3

con = sqlite3.connect(":memory:")
cur = con.cursor()

AUSTRIA = u"\xd6sterreich"

# by default, rows are returned as Unicode
cur.execute("select ?", (AUSTRIA,))
row = cur.fetchone()
assert row[0] == AUSTRIA

# but we can make sqlite3 always return bytestrings ...
con.text_factory = str
cur.execute("select ?", (AUSTRIA,))
row = cur.fetchone()
assert type(row[0]) is str
# the bytestrings will be encoded in UTF-8, unless you stored garbage in the
# database ...
assert row[0] == AUSTRIA.encode("utf-8")

# we can also implement a custom text_factory ...
# here we implement one that will ignore Unicode characters that cannot be
# decoded from UTF-8
con.text_factory = lambda x: unicode(x, "utf-8", "ignore")
cur.execute("select ?", ("this is latin1 and would normally create errors" +
                         u"\xe4\xf6\xfc".encode("latin1"),))
row = cur.fetchone()
assert type(row[0]) is unicode

# sqlite3 offers a built-in optimized text_factory that will return bytestring
# objects, if the data is in ASCII only, and otherwise return unicode objects
con.text_factory = sqlite3.OptimizedUnicode
cur.execute("select ?", (AUSTRIA,))
row = cur.fetchone()
assert type(row[0]) is unicode

cur.execute("select ?", ("Germany",))
row = cur.fetchone()
assert type(row[0]) is str

total_changes

返回自数据库连接打开以来已修改,插入或删除的数据库行总数。

iterdump

返回一个以SQL文本格式转储数据库的迭代器。在保存内存数据库以供日后恢复时很有用。该函数提供了与sqlite3 shell中的.dump命令相同的功能。

2.6版本中的新功能。

例:

代码语言:javascript
复制
# Convert file existing_db.db to SQL dump file dump.sql
import sqlite3, os

con = sqlite3.connect('existing_db.db')
with open('dump.sql', 'w') as f:
    for line in con.iterdump():
        f.write('%s\n' % line)

3.游标对象

class sqlite3.Cursor

一个Cursor实例具有以下属性和方法。

execute(sql[, parameters])

执行一条SQL语句。SQL语句可能是参数化的(即占位符而不是SQL文字)。该sqlite3模块支持两种占位符:问号(qmark样式)和命名占位符(命名样式)。

以下是两种样式的示例:

代码语言:javascript
复制
import sqlite3

con = sqlite3.connect(":memory:")
cur = con.cursor()
cur.execute("create table people (name_last, age)")

who = "Yeltsin"
age = 72

# This is the qmark style:
cur.execute("insert into people values (?, ?)", (who, age))

# And this is the named style:
cur.execute("select * from people where name_last=:who and age=:age", {"who": who, "age": age})

print cur.fetchone()

execute()只会执行一条SQL语句。如果您尝试使用它执行多个语句,则会引发警告。使用executescript(),如果你想用一个调用执行多个SQL语句。

executemany(sql, seq_of_parameters)

针对在序列sql中找到的所有参数序列或映射执行SQL命令。该sqlite3模块还允许使用迭代器产生参数而不是序列。

代码语言:javascript
复制
import sqlite3

class IterChars:
    def __init__(self):
        self.count = ord('a')

    def __iter__(self):
        return self

    def next(self):
        if self.count > ord('z'):
            raise StopIteration
        self.count += 1
        return (chr(self.count - 1),) # this is a 1-tuple

con = sqlite3.connect(":memory:")
cur = con.cursor()
cur.execute("create table characters(c)")

theIter = IterChars()
cur.executemany("insert into characters(c) values (?)", theIter)

cur.execute("select c from characters")
print cur.fetchall()

以下是使用生成器的较简单示例:

代码语言:javascript
复制
import sqlite3
import string

def char_generator():
    for c in string.lowercase:
        yield (c,)

con = sqlite3.connect(":memory:")
cur = con.cursor()
cur.execute("create table characters(c)")

cur.executemany("insert into characters(c) values (?)", char_generator())

cur.execute("select c from characters")
print cur.fetchall()

executescript(sql_script)

这是一次执行多个SQL语句的非标准便利方法。它首先发布一个COMMIT语句,然后执行它作为参数获取的SQL脚本。

sql_script可以是一个字节串或一个Unicode字符串。

例:

代码语言:javascript
复制
import sqlite3

con = sqlite3.connect(":memory:")
cur = con.cursor()
cur.executescript("""
    create table person(
        firstname,
        lastname,
        age
    );

    create table book(
        title,
        author,
        published
    );

    insert into book(title, author, published)
    values (
        'Dirk Gently''s Holistic Detective Agency',
        'Douglas Adams',
        1987
    );
    """)

fetchone()

获取查询结果集的下一行,返回单个序列,或者None没有更多数据可用时。

fetchmany([size=cursor.arraysize])

获取查询结果的下一组行,并返回一个列表。没有更多行可用时返回空列表。

每次调用要获取的行数由size参数指定。如果没有给出,游标的数组大小决定了要获取的行数。该方法应该尝试获取大小参数所指示的行数。如果由于指定的行数不可用而无法执行此操作,则可能会返回更少的行。

请注意,大小参数涉及性能方面的考虑因素。为了获得最佳性能,通常最好使用arraysize属性。如果使用size参数,那么最好从一次fetchmany()调用到下一次调用保持相同的值。

fetchall()

获取查询结果的所有(剩余)行,并返回一个列表。请注意,游标的arraysize属性可能会影响此操作的性能。没有行可用时返回空列表。

rowcount

虽然模块的Cursorsqlite3实现了这个属性,但数据库引擎自己对确定“受影响的行”/“所选择的行”的支持是古怪的。

对于executemany()陈述,修改的数量被总结成rowcount

按照Python DB API Spec的要求,rowcount如果executeXX()游标上没有执行任何操作,或者上一次操作的行数不能被界面确定,则属性“为-1 ”。这包括SELECT语句,因为我们无法确定查询产生的行数,直到获取所有行。

使用3.6.5之前的SQLite版本,rowcount如果您DELETE FROM table没有任何条件,则设置为0 。

lastrowid

此只读属性提供最后修改行的rowid。只有在您INSERT使用该execute()方法发布声明时才会设置。对于调用以外INSERTexecutemany()调用时的操作,lastrowid设置为None

description

此只读属性提供最后一个查询的列名称。为了与Python DB API保持兼容,它会为每个元组的最后六项所在的每列返回一个7元组None

它被设置为SELECT没有任何匹配行的语句。

connection

这个只读属性提供ConnectionCursor对象使用的SQLite数据库。一个Cursor通过调用创建的对象con.cursor()将有一个connection引用属性CON

代码语言:javascript
复制
>>> con = sqlite3.connect(":memory:")
>>> cur = con.cursor()
>>> cur.connection == con
True

4.行对象

class sqlite3.Row

Row实例用作一个高度优化的row_factory用于Connection对象。它试图模仿大部分功能中的元组。

它支持按列名和索引进行映射访问,迭代,表示,相等性测试和len()

如果两个Row对象具有完全相同的列并且它们的成员相等,则它们相等。

在版本2.6中进行了更改:添加了迭代和等式(hashability)。

keys()

此方法返回列名称的列表。在查询之后,它立即成为每个元组的第一个成员Cursor.description

2.6版本中的新功能。

我们假设我们按照上面给出的例子初始化一个表格:

代码语言:javascript
复制
conn = sqlite3.connect(":memory:")
c = conn.cursor()
c.execute('''create table stocks
(date text, trans text, symbol text,
 qty real, price real)''')
c.execute("""insert into stocks
          values ('2006-01-05','BUY','RHAT',100,35.14)""")
conn.commit()
c.close()

现在我们插入Row

代码语言:javascript
复制
>>> conn.row_factory = sqlite3.Row
>>> c = conn.cursor()
>>> c.execute('select * from stocks')
<sqlite3.Cursor object at 0x7f4e7dd8fa80>
>>> r = c.fetchone()
>>> type(r)
<type 'sqlite3.Row'>
>>> r
(u'2006-01-05', u'BUY', u'RHAT', 100.0, 35.14)
>>> len(r)
5
>>> r[2]
u'RHAT'
>>> r.keys()
['date', 'trans', 'symbol', 'qty', 'price']
>>> r['qty']
100.0
>>> for member in r:
...     print member
...
2006-01-05
BUY
RHAT
100.0
35.14

5. SQLite和Python类型

5.1。介绍

SQLite的原生支持以下几种类型:NULLINTEGERREALTEXTBLOB

因此可以将以下Python类型发送给SQLite,而不会有任何问题:

Python类型

SQLite类型

没有

空值

INT

整数

long

整数

浮动

真实

str(UTF8编码)

文本

统一

文本

缓冲

BLOB

这是默认情况下SQLite类型转换为Python类型的方式:

SQLite类型

Python类型

空值

没有

整数

int或long,这取决于大小

真实

浮动

文本

取决于text_factory,默认情况下是unicode

BLOB

缓冲

sqlite3模块的类型系统可以通过两种方式进行扩展:可以通过对象适配将其他Python类型存储在SQLite数据库中,并且可以让sqlite3模块通过转换器将SQLite类型转换为不同的Python类型。

5.2。使用适配器在SQLite数据库中存储其他Python类型

如前所述,SQLite本身只支持一组有限的类型。要将其他Python类型与SQLite一起使用,您必须它们调整为sqlite3模块支持的SQLite类型之一:NoneType,int,long,float,str,unicode,buffer之一。

有两种方法可以使sqlite3模块将自定义的Python类型改为支持的类型之一。

5.2.1。让你的对象适应自己

如果你自己写课程,这是一个很好的方法。假设你有这样的课程:

代码语言:javascript
复制
class Point(object):
    def __init__(self, x, y):
        self.x, self.y = x, y

现在您想要将该点存储在单个SQLite列中。首先,您必须首先选择一种支持的类型来表示该点。我们只需使用str并使用分号分隔坐标。然后你需要给你的班级一个__conform__(self, protocol)必须返回转换后的值的方法。参数协议将会是PrepareProtocol

代码语言:javascript
复制
import sqlite3

class Point(object):
    def __init__(self, x, y):
        self.x, self.y = x, y

    def __conform__(self, protocol):
        if protocol is sqlite3.PrepareProtocol:
            return "%f;%f" % (self.x, self.y)

con = sqlite3.connect(":memory:")
cur = con.cursor()

p = Point(4.0, -3.2)
cur.execute("select ?", (p,))
print cur.fetchone()[0]

5.2.2。注册可调用的适配器

另一种可能性是创建一个将类型转换为字符串表示并将函数注册的函数register_adapter()

注释

适应的类型/类别必须是新式类别,即它必须具有object作为其基础之一。

代码语言:javascript
复制
import sqlite3

class Point(object):
    def __init__(self, x, y):
        self.x, self.y = x, y

def adapt_point(point):
    return "%f;%f" % (point.x, point.y)

sqlite3.register_adapter(Point, adapt_point)

con = sqlite3.connect(":memory:")
cur = con.cursor()

p = Point(4.0, -3.2)
cur.execute("select ?", (p,))
print cur.fetchone()[0]

sqlite3模块有两个用于Python内置datetime.datedatetime.datetime类型的默认适配器。现在让我们假设我们想存储datetime.datetime不是ISO表示的对象,而是作为一个Unix时间戳。

代码语言:javascript
复制
import sqlite3
import datetime, time

def adapt_datetime(ts):
    return time.mktime(ts.timetuple())

sqlite3.register_adapter(datetime.datetime, adapt_datetime)

con = sqlite3.connect(":memory:")
cur = con.cursor()

now = datetime.datetime.now()
cur.execute("select ?", (now,))
print cur.fetchone()[0]

5.3。将SQLite值转换为自定义Python类型

编写适配器可让您将自定义Python类型发送到SQLite。但为了让它变得非常有用,我们需要让Python到SQLite来Python的往返工作。

输入转换器。

让我们回到Point课堂上。我们将通过分号分隔的x和y坐标存储为SQLite中的字符串。

首先,我们将定义一个转换器函数,该函数接受字符串作为参数并Point从中构造一个对象。

注意

无论使用哪种数据类型将值发送给SQLite,转换器函数总是使用字符串调用。

代码语言:javascript
复制
def convert_point(s):
    x, y = map(float, s.split(";"))
    return Point(x, y)

现在您需要让sqlite3模块知道您从数据库中选择的内容实际上是一个点。有两种方法可以做到这一点:

  • 隐式地通过声明的类型
  • 显式地通过列名称

这两种方法在节模块函数和常数描述,在用于常量的条目PARSE_DECLTYPESPARSE_COLNAMES

以下示例说明了这两种方法。

代码语言:javascript
复制
import sqlite3

class Point(object):
    def __init__(self, x, y):
        self.x, self.y = x, y

    def __repr__(self):
        return "(%f;%f)" % (self.x, self.y)

def adapt_point(point):
    return "%f;%f" % (point.x, point.y)

def convert_point(s):
    x, y = map(float, s.split(";"))
    return Point(x, y)

# Register the adapter
sqlite3.register_adapter(Point, adapt_point)

# Register the converter
sqlite3.register_converter("point", convert_point)

p = Point(4.0, -3.2)

#########################
# 1) Using declared types
con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES)
cur = con.cursor()
cur.execute("create table test(p point)")

cur.execute("insert into test(p) values (?)", (p,))
cur.execute("select p from test")
print "with declared types:", cur.fetchone()[0]
cur.close()
con.close()

#######################
# 1) Using column names
con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_COLNAMES)
cur = con.cursor()
cur.execute("create table test(p)")

cur.execute("insert into test(p) values (?)", (p,))
cur.execute('select p as "p [point]" from test')
print "with column names:", cur.fetchone()[0]
cur.close()
con.close()

5.4。默认适配器和转换器

日期时间模块中有日期和日期时间类型的默认适配器。它们将作为ISO日期/ ISO时间戳发送到SQLite。

默认转换器的名称为“date”,datetime.date名称为“timestamp” datetime.datetime

这样,在大多数情况下,您可以使用Python中的日期/时间戳,而不需要额外的操作。适配器的格式也与实验性SQLite日期/时间函数兼容。

以下示例演示了这一点。

代码语言:javascript
复制
import sqlite3
import datetime

con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
cur = con.cursor()
cur.execute("create table test(d date, ts timestamp)")

today = datetime.date.today()
now = datetime.datetime.now()

cur.execute("insert into test(d, ts) values (?, ?)", (today, now))
cur.execute("select d, ts from test")
row = cur.fetchone()
print today, "=>", row[0], type(row[0])
print now, "=>", row[1], type(row[1])

cur.execute('select current_date as "d [date]", current_timestamp as "ts [timestamp]"')
row = cur.fetchone()
print "current_date", row[0], type(row[0])
print "current_timestamp", row[1], type(row[1])

如果存储在SQLite中的时间戳记的分数部分长于6个数字,则其值将被时间戳转换器截断为微秒精度。

6.控制交易

默认情况下,sqlite3模块在数据修改语言(DML)语句(即INSERT/ UPDATE/ DELETE/ REPLACE)之前隐式打开事务,并在非DML非查询语句(即非SELECT上述任何内容)之前隐式提交事务。

所以,如果你是一个事务中,并发出这样的命令CREATE TABLE ...VACUUMPRAGMA,该sqlite3模块将隐式执行该命令之前提交。这样做有两个原因。首先,这些命令中的某些命令在事务中不起作用。另一个原因是sqlite3需要跟踪事务状态(如果事务处于活动状态)。

你可以控制BEGINsqlite3通过调用isolation_level参数connect()或通过isolation_level连接属性隐式执行哪种语句(或者根本不执行)。

如果您想要自动提交模式,请设置isolation_levelNone

否则,将其保留为默认值,这将导致一个简单的“BEGIN”语句,或将其设置为SQLite支持的隔离级别之一:“DEFERRED”,“IMMEDIATE”或“EXCLUSIVE”。

7.高效地使用sqlite3

7.1。使用快捷方式

使用非标准的execute()executemany()并且executescript()该方法的Connection对象,您的代码可以更简洁,因为你不必创建(通常是多余的)书面Cursor明确对象。相反,Cursor对象是隐式创建的,这些快捷方法返回游标对象。这样,您可以执行一个SELECT语句并直接使用该Connection对象上的一次调用直接对其进行迭代。

代码语言:javascript
复制
import sqlite3

persons = [
    ("Hugo", "Boss"),
    ("Calvin", "Klein")
    ]

con = sqlite3.connect(":memory:")

# Create the table
con.execute("create table person(firstname, lastname)")

# Fill the table
con.executemany("insert into person(firstname, lastname) values (?, ?)", persons)

# Print the table contents
for row in con.execute("select firstname, lastname from person"):
    print row

print "I just deleted", con.execute("delete from person").rowcount, "rows"

7.2。按名称而不是按索引访问列

sqlite3模块的一个有用功能是sqlite3.Row设计用作行工厂的内置类。

使用此类包装的行可以通过索引(如元组)访问,也可以通过名称不区分大小写:

代码语言:javascript
复制
import sqlite3

con = sqlite3.connect(":memory:")
con.row_factory = sqlite3.Row

cur = con.cursor()
cur.execute("select 'John' as name, 42 as age")
for row in cur:
    assert row[0] == row["name"]
    assert row["name"] == row["nAmE"]
    assert row[1] == row["age"]
    assert row[1] == row["AgE"]

7.3。使用连接作为上下文管理器

2.6版本中的新功能。

连接对象可以用作自动提交或回滚事务的上下文管理器。如果发生异常,交易将回滚; 否则,交易承诺:

代码语言:javascript
复制
import sqlite3

con = sqlite3.connect(":memory:")
con.execute("create table person (id integer primary key, firstname varchar unique)")

# Successful, con.commit() is called automatically afterwards
with con:
    con.execute("insert into person(firstname) values (?)", ("Joe",))

# con.rollback() is called after the with block finishes with an exception, the
# exception is still raised and must be caught
try:
    with con:
        con.execute("insert into person(firstname) values (?)", ("Joe",))
except sqlite3.IntegrityError:
    print "couldn't add Joe twice"

8.常见问题

8.1。多线程

较旧的SQLite版本在共享线程之间的连接时遇到了问题。这就是为什么Python模块不允许在线程之间共享连接和游标的原因。如果你仍然尝试这样做,你将在运行时得到一个异常。

唯一的例外是调用该interrupt()方法,这只有在从不同的线程调用时才有意义。

1

(1,2)默认情况下,sqlite3模块不是由可加载扩展支持构建的,因为某些平台(特别是Mac OS X)具有无需此功能编译的SQLite库。要获得可加载的扩展支持,您必须修改setup.py并删除设置SQLITE_OMIT_LOAD_EXTENSION的行。

扫码关注腾讯云开发者

领取腾讯云代金券