sqlite3 遵循Python PDB-API 2.0 接口规范的SQLite库(2)
marlboro027
|
1#
marlboro027 发表于 2007-04-03 01:27
sqlite3 遵循Python PDB-API 2.0 接口规范的SQLite库(2)
13.13.3 Cursor 对象
一个cursor实例拥有如下的属性与方法: l execute( sql, [parameters]) 执行一个SQL语句。SQL语句可以是参数化的(用占位符来代替具体的字面值)。Sqlite3支持两种类型的占位符:问号与命名。 下面的例子展示了如果使用问号: import sqlite3 con = sqlite3.connect("mydb") cur = con.cursor() who = "Yeltsin" age = 72 cur.execute("select name_last, age from people where name_last=? and age=?", (who, age)) print cur.fetchone() 下面的例子展示了如何使用命名: import sqlite3 con = sqlite3.connect("mydb") cur = con.cursor() who = "Yeltsin" age = 72 cur.execute("select name_last, age from people where name_last=:who and age=:age", {"who": who, "age": age}) print cur.fetchone() 方法execute()只能执行一条单独的SQL语句。如果你想要执行超过一条语句,将会引发一个warning。可以使用executescript()方法来在一次调用中执行多条SQL语句。 l executemany( sql, seq_of_parameters) 使用seq_of_parameters中的值来多次调用SQL语句,seq_of_parameters可以是序列或映射。Sqlite3模块也支持会用迭代器生成参数来代替使用序列。 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() 下面是一个使用迭代生成器的例子: import sqlite3 def char_generator(): import string for c in string.letters[:26]: 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() l executescript( sql_script) 这是一个非标准的可以在一次调用方便地执行多句SQL语句的方法。它首先发出一个COMMIT语句,之后执行SQL语句。 参数sql_script可以是一个bytestring或Unicode编码的字符串。 示例如下: 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 ); """) l rowcount 虽然sqlite3模块中的cursor类实现了这个属性,但是数据库引擎自己对于“行数”统计的能力还是要强得多。 对于SELECT语句,rowcount总是为None,因为我们在所有行被取回前不能确定总的行数。 对于DELETE语句,当你在DELETE FROM 语句中没有使用任何条件的话,rowcount为0, 对于executemany语句,所有进行修改的行数都会被总计到rowcount中。 作为Python DB API规范的要求,在没有executeXX()方法被执行的情况下,或者在最近的一次操作中不能确定行数,则rowcount属性值将会是-1。 13.13.4 SQLite与Python类型 13.13.4.1 简介 SQLite只支持以下的内建类型:NULL, INTEGER, REAL, TEXT, BLOB. 因此,以下的Python类型可以毫无问题地传入SQLite。 Python type SQLite type None NULL int INTEGER long INTEGER float REAL str (UTF8-encoded) TEXT unicode TEXT buffer BLOB 下表说明了默认情况下SQLite类型如何被转换为Python类型: SQLite type Python type NULL None INTEGER int or long, depending on size REAL float TEXT depends on text_factory, unicode by default BLOB buffer Sqlite3的类型系统可以有两种扩展方法:你可以通过对象适配器来将Python对象转换并存储到Sqlite数据库中,可以通过转换器将SQLite类型转换为Python对象。 13.13.4.2 使用适配器将其它Pyton类型转换到SQLite数据库中 象前面所说,SQLite本身只支持很少的类型。要在SQLite程序中使用其它的Python类型,你必须要将它们适配成为一种SQLite支持的数据类型:NoneType, int, long, float, str, unicode, buffer。 Sqlite3模块使用Python对象适配器,就象在PEP246中所述的一样。所使用的协议为PrepareProtocol。 有两种方法可以将一个定制Python类型适配成为SQLite支持的类型。 13.13.4.2.1 让你的对象具有自我适配能力 对于你自己定义的类,这是个比较好的办法,让我们假设你有一个类: class Point(object): def __init__(self, x, y): self.x, self.y = x, y 现在你想将point信息存储在一个SQLite列中。首先,你要选择一个SQLite支持的类型来表示这个point对象。我们可以使用str类型,并坐标X与Y之间加入一个分号。然后,你需要在类中定义一个方法 __conform__(self, protocol),用来返回转换后的值。记住参数protocol要使用PrepareProtocol。 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] 13.13.4.2.2 注册一个适配器callable 另外一种可能是创建一个函数来将类型转换成表示它的字符串,并通过register_adapter来注册它。 注意:被适配的类型/类必须是一个新式样的class,就是说object类必须是它的基类之一。 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.date及datetime.datetime的默认适配器。现在假设我们想要存储datetime.datetime对象。 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] 13.13.4.3 将SQLite值转换为Python定制类型 通过编写一个适配器可以让你将Python类型送入SQLite。但是要想让它发挥作用,我们还得完成从SQLite转换到Python的过程。 让我们回到Point 类。我们在SQLite中用分号分隔的格式存储了X与Y坐标信息。 首先,我们将定义一个转换函数,接收一个字符串作为参数,并从中构建一个Point对象。 注意:转换器函数总是用一个字符串参数来调用,而不管当初你送入SQLite的类型。 注意:转换器的名字是大小写敏感的。 def convert_point(s): x, y = map(float, s.split(";")) return Point(x, y) 现在你要让sqlite3模块知道数据库中的那列数据是一个Point。有两种方法来实现: l 通过声明类型隐含地表示 l 通过列名字明确地表示 两种方法都由模块常量来描述,还记得在13.13.1部分,常量PARSE_DECLTYPES与PARSE_COLNAMES。 下面的例子展示了这两种方法: 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) 使用声明类型 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) 使用列名字 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() 13.13.4.4 默认适配器与转换器 在datatime模块中有针对date与datetime的默认适配器。它们将会被按ISO date或ISO timestamps格式被传入到SQLite中。 注册好的默认转换器为datetime.date类型使用名字date,为类型datetime.datetime使用名字timestamp。 这样,大部分情况下,你可以在Python中使用date或timestamps而不需要任何额外的处理。同时,适配器使用的格式也与正在试验中的SQLite date/time函数保持兼容。 以下是示例: 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]) 13.13.5 控制事务 默认情况,sqlite3模块在数据修改语句(DML,如INSERT,UPDATE, DELETE, REPLACE)开始执行前隐含地打开一个事务,并在开始一个非数据修改语句(任何非SELECT, INSERT, UPDATE, DELETE, REPLACE的语句)前确认事务。 所以若你在一个事务内,并且发出一个像CREATE TABLE,VACUUM,PRAGMA这样的命令,sqlite3将会在执行它们之前隐含地结束事务。有两个理由要这样做,第一个是一些这样的命令不能在事务中处理,另一个是pysqlite必须要对事务状态保持跟踪。 通过connect调用时的isolation_level,或者connection对象的isolation_level属性,我们都可以控制pysqlite隐含使用那种类型的”BEGIN”语句(或根本不执行此类语句)。 若你想使用autocommit模式,将isolation_level设置为None。 要不然保持isolation_level的默认值,这将导致一个清楚的”BEGIN”语句,或者设成SQLite支持的值之一:DEFERRED, IMMEDIATE or EXCLUSIVE。 因为sqlite3模块需要对事务状态保持跟踪,你还不应该在SQL中使用OR ROLLBACK或ON CONFLICT ROLLBACK语句,而要捕捉IntegrityError错误,再由你自己调用connection的回滚方法。 13.13.6 高效使用pysqlite 13.13.6.1 使用快捷方法 使用connection对象的非标准的execute,executemany,及executescript方法,可以写出更加简洁的代码,因为不再需要明确地创建Cursor对象。Cursor对象会被隐含地创建,并由这些方法执行完后返回。我们可以只用一次调用就可以执行一句SELECT语句,并直接迭代处理结果集。 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 # Using a dummy WHERE clause to not let SQLite take the shortcut table deletes. print "I just deleted", con.execute("delete from person where 1=1").rowcount, "rows" 13.13.6.2 使用列名而不是列索引来访问列数据 Sqlite3模块一个有用的功能是内建的sqlite3.Row类,它被设计作为一个row_factory。 用这个类封闭的row可以通过列索引(像tuple)或列名(大小写敏感)来访问。 import sqlite3 con = sqlite3.connect("mydb") con.row_factory = sqlite3.Row cur = con.cursor() cur.execute("select name_last, age from people") for row in cur: assert row[0] == row["name_last"] assert row["name_last"] == row["nAmE_lAsT"] assert row[1] == row["age"] assert row[1] == row["AgE"] |