我可以在一个sqlite3文本字段中挑选一个python字典吗?

时间:2008-10-13 19:11:14

标签: python sqlite pickle

我应该注意哪些问题?我可以将它存储在文本字段中,还是需要使用blob? (我对pickle或sqlite都不太熟悉,所以我想确保我用一些高级设计思想咆哮着正确的树。)

14 个答案:

答案 0 :(得分:46)

我也需要达到同样的目的。

我发现它让我非常头疼,然后我终于想出了thanks to this post,如何让它以二进制格式运行。

要插入/更新:

pdata = cPickle.dumps(data, cPickle.HIGHEST_PROTOCOL)
curr.execute("insert into table (data) values (:data)", sqlite3.Binary(pdata))

您必须指定转储的第二个参数以强制进行二进制酸洗 另请注意 sqlite3.Binary ,使其适合BLOB字段。

要检索数据:

curr.execute("select data from table limit 1")
for row in curr:
  data = cPickle.loads(str(row['data']))

当检索BLOB字段时,sqlite3得到一个'缓冲区'python类型,需要在传递给loads方法之前使用 str 进行严格修改。

答案 1 :(得分:17)

如果要存储pickle对象,则需要使用blob,因为它是二进制数据。但是,您可以使用base64对pickle对象进行编码,以获取可以存储在文本字段中的字符串。

但一般来说,做这种事情表明设计不好,因为你存储的是不透明数据,你就失去了使用SQL对数据进行任何有用操作的能力。虽然我不知道你在做什么,但我真的无法对它进行道德呼唤。

答案 2 :(得分:5)

我写了一个关于这个想法的博客,除了一个泡菜,我使用了json,因为我希望它可以与perl和其他程序互操作。

http://writeonly.wordpress.com/2008/12/05/simple-object-db-using-json-and-python-sqlite/

在架构上,这是获取任意数据结构的持久性,事务等的快速而肮脏的方法。我发现这个组合在我想要持久性时非常有用,并且不需要在sql层中用数据做很多事情(或者在sql中处理它非常复杂,而且对于生成器来说很简单)。

代码本身非常简单:

#  register the "loader" to get the data back out.
sqlite3.register_converter("pickle", cPickle.loads) 

然后,当你想将它转储到数据库中时,

p_string = p.dumps( dict(a=1,b=[1,2,3]))  
conn.execute(''' 
   create table snapshot( 
      id INTEGER PRIMARY KEY AUTOINCREMENT, 
        mydata pickle); 
''')  

conn.execute(''' 
    insert into snapshot values 
    (null, ?)''', (p_string,))
''')

答案 3 :(得分:3)

Pickle有文本和二进制输出格式。如果使用基于文本的格式,则可以将其存储在TEXT字段中,但如果使用(更有效的)二进制格式,则必须是BLOB。

答案 4 :(得分:2)

由于Pickle可以将对象图转储为字符串,因此应该可以。

请注意,SQLite中的TEXT字段使用数据库编码,因此您可能需要在取消选择之前将其转换为简单字符串。

答案 5 :(得分:2)

如果可以腌制字典,它也可以存储在text / blob字段中。

请注意无法腌制的字典(也就是说包含不可剔除的对象)。

答案 6 :(得分:2)

是的,你可以将一个pickled对象存储在SQLite3数据库的TEXT或BLOB字段中,正如其他人所解释的那样。

请注意,某些对象无法腌制。内置容器类型可以(dict,set,list,tuple等)。但是一些对象(例如文件句柄)指的是它们自己的数据结构外部的状态,而其他扩展类型也有类似的问题。

由于字典可以包含任意嵌套数据结构,因此它可能不具备可选性。

答案 7 :(得分:2)

我必须同意这里的一些评论。小心并确保你真的想在数据库中保存pickle数据,这可能是一种更好的方法。

无论如何,我在过去尝试在sqlite db中保存二进制数据时遇到了麻烦。 显然你必须使用sqlite3.Binary()为sqlite准备数据。

以下是一些示例代码:

query = u'''insert into testtable VALUES(?)'''
b = sqlite3.Binary(binarydata)
cur.execute(query,(b,))
con.commit()

答案 8 :(得分:1)

SpoonMeiser是正确的,你需要有充分的理由来挑选数据库。

编写使用SQLite实现持久性的Python对象并不困难。然后,您也可以使用SQLite CLI来调整数据。根据我的经验值得花费额外的工作,因为许多调试和管理功能可以简单地从CLI执行,而不是编写特定的Python代码。

在项目的早期阶段,我做了你的建议并最终用每个业务对象的Python类重写(注意:我没有为每个表说过!)这样应用程序的主体可以专注于“需要做什么”,而不是“如何”完成。

答案 9 :(得分:1)

另一个选择,考虑到你的要求是保存一个字典,然后为用户的“观看乐趣”吐出来,就是使用shelve模块,它可以让任何可选择的数据保存到文件中。 python文档是here

答案 10 :(得分:1)

根据您的工作情况,您可能需要查看shove模块。它做了类似的事情,它将Python对象自动存储在sqlite数据库(以及各种其他选项)中并伪装成字典(就像shelve模块一样)。

答案 11 :(得分:1)

可以将对象数据存储为pickle dump,jason等,但也可以对它们进行索引,限制它们并运行使用这些索引的选择查询。以下是元组的示例,可以轻松应用于任何其他python类。所有需要的都在python sqlite3文档中解释(有人已发布链接)。无论如何,在以下示例中将它们全部放在一起:

import sqlite3
import pickle

def adapt_tuple(tuple):
    return pickle.dumps(tuple)    

sqlite3.register_adapter(tuple, adapt_tuple)    #cannot use pickle.dumps directly because of inadequate argument signature 
sqlite3.register_converter("tuple", pickle.loads)

def collate_tuple(string1, string2):
    return cmp(pickle.loads(string1), pickle.loads(string2))

#########################
# 1) Using declared types
con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES)

con.create_collation("cmptuple", collate_tuple)

cur = con.cursor()
cur.execute("create table test(p tuple unique collate cmptuple) ")
cur.execute("create index tuple_collated_index on test(p collate cmptuple)")

cur.execute("select name, type  from sqlite_master") # where type = 'table'")
print(cur.fetchall())

p = (1,2,3)
p1 = (1,2)

cur.execute("insert into test(p) values (?)", (p,))
cur.execute("insert into test(p) values (?)", (p1,))
cur.execute("insert into test(p) values (?)", ((10, 1),))
cur.execute("insert into test(p) values (?)", (tuple((9, 33)) ,))
cur.execute("insert into test(p) values (?)", (((9, 5), 33) ,))

try:
    cur.execute("insert into test(p) values (?)", (tuple((9, 33)) ,))
except Exception as e:
    print e

cur.execute("select p from test order by p")
print "\nwith declared types and default collate on column:"
for raw in cur:
    print raw

cur.execute("select p from test order by p collate cmptuple")
print "\nwith declared types collate:"
for raw in cur:
    print raw

con.create_function('pycmp', 2, cmp)

print "\nselect grater than using cmp function:"
cur.execute("select p from test where pycmp(p,?) >= 0", ((10, ),) )
for raw in cur:
    print raw

cur.execute("explain query plan select p from test where p > ?", ((3,)))
for raw in cur:
    print raw 

print "\nselect grater than using collate:"
cur.execute("select p from test where p > ?", ((10,),) )
for raw in cur:
    print raw  

cur.execute("explain query plan select p from test where p > ?", ((3,)))
for raw in cur:
    print raw

cur.close()
con.close()

答案 12 :(得分:0)

在SourceForge上查看此解决方案:

y_serial.py module ::使用SQLite的仓库Python对象

“序列化+持久性::在几行代码中,将Python对象压缩并注释为SQLite;然后通过关键字按时间顺序检索它们,而不使用任何SQL。最有用的”标准“模块,用于存储无模式数据的数据库。“

http://yserial.sourceforge.net

答案 13 :(得分:0)

许多应用程序都将sqlite3用作SQLAlchemy的后端,因此,自然地,也可以在SQLAlchemy框架中问这个问题(这就是我遇到的问题)。

要执行此操作,将需要定义一列,其中需要存储泡菜数据以存储“ PickleType”数据。实现非常简单:

from sqlalchemy import PickleType, Integer
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
import pickle

Base= declarative_base()

class User(Base):
    __tablename__= 'Users'

    id= Column(Integer, primary_key= True)
    user_login_data_array= Column(PickleType)

login_information= {'User1':{'Times': np.arange(0,20),
                             'IP': ['123.901.12.189','123.441.49.391']}}

engine= create_engine('sqlite:///memory:',echo= False) 

Base.metadata.create_all(engine)
Session_maker= sessionmaker(bind=engine)
Session= Session_maker()

# The pickling here is very intuitive! Just need to have 
# defined the column "user_login_data_array" to take pickletype data.

pickled_login_data_array= pickle.dumps(login_information)
user_object_to_add= User(user_login_data_array= pickled_login_data_array)

Session.add(user_object_to_add)
Session.commit()

(我并不是说这个示例最适合使用泡菜,因为其他人已经注意到了问题。)