【问题标题】:Proper way to store GUID in sqlite在 sqlite 中存储 GUID 的正确方法
【发布时间】:2013-09-20 04:54:19
【问题描述】:

我希望使用 C# 和 python 客户端将 GUID 存储在我的 SQLite 数据库中。

创建数据库,插入一行,将 GUID 存储为字符串:

conn = sqlite3.connect(filename)

c = conn.cursor()

# Create the table. Yes, I know GUID isn't a real SQLite datatype.
c.execute('CREATE TABLE test (guid GUID PRIMARY KEY, name text)')

u = uuid.uuid4()
print u
t = (str(u), 'foo')
c.execute('INSERT INTO test VALUES (?,?)', t)

conn.commit()
conn.close()

获取:

# ...
c.execute('SELECT * FROM test WHERE guid = "c1332103-6031-4ff7-b610-f8f3b940fa66"')
print c.fetchone()

这一切都完美无缺。使用 UUID 的默认 Python __str__ 表示效果很好。

C:\Users\Jonathon>makedb.py test.db
c1332103-6031-4ff7-b610-f8f3b940fa66

C:\Users\Jonathon>opendb.py test.db
(u'c1332103-6031-4ff7-b610-f8f3b940fa66', u'foo')

我对使用SQLite Expert 产生了疑问。 SQLite Expert 似乎对我的 GUID 数据类型声明很满意:

但是,如果我编辑一行:

它似乎改变了数据类型!我之前的SELECT 产生None,如果我SELECT *,我发现它不再是一个简单的unicode 字符串:

C:\Users\Jonathon>opendb.py test.db
(<read-write buffer ptr 0x02239520, size 16 at 0x02239500>, u'foo')

查看磁盘上的数据,可以看到 GUID 是二进制存储的,SQLite Expert 触摸后:

之前 - GUID 是 ASCII 文本:

之后——之前的数据是垃圾,存在二进制版本的GUID:


那么“正确” 在 SQLite 中存储 GUID 的方法是什么,特别是使用 Python?稍后,我也会让 C# 代码与之交互,并希望确保我以“正确”的方式进行操作。

【问题讨论】:

  • 不要将 GUID/UUID 设为主键,使用单独的内部 id - 特别是如果您打算建立外键关系,请参阅 mail-archive.com/sqlite-users%40sqlite.org/msg27377.html
  • SQLite(以及 SQLite 专家)对 GUID 类型感到满意,因为 SQLite 不强制类型(INTEGER PRIMARY KEY 字段除外)。
  • 嗯,似乎有意见冲突:stackoverflow.com/questions/3272013/…
  • 是的,但显然 SQLite Expert 对其进行了特殊处理。我应该将我的 GUID 存储为二进制 blob 而不是字符串吗?如果是这样的话,似乎会让 SELECT 变得更棘手。

标签: python sqlite


【解决方案1】:

基本上可以在 Python 中为 sqlite3 添加对 GUID 数据类型的支持。可以注册转换函数:

要使用这些,您需要为connect()detect_types 参数传递一个参数。

例子:

import sqlite3
import uuid

sqlite3.register_converter('GUID', lambda b: uuid.UUID(bytes_le=b))
sqlite3.register_adapter(uuid.UUID, lambda u: buffer(u.bytes_le))

conn = sqlite3.connect('test.db', detect_types=sqlite3.PARSE_DECLTYPES)

c = conn.cursor()
c.execute('CREATE TABLE test (guid GUID PRIMARY KEY, name TEXT)')

data = (uuid.uuid4(), 'foo')
print 'Input Data:', data
c.execute('INSERT INTO test VALUES (?,?)', data)

c.execute('SELECT * FROM test')
print 'Result Data:', c.fetchone()

输出:

Input Data: (UUID('59cc2646-8666-4fb3-9f57-fe76e22603c0'), 'foo')
Result Data: (UUID('59cc2646-8666-4fb3-9f57-fe76e22603c0'), u'foo')

结果:

  • 我将uuid.UUID 对象直接传递给execute()。适配器 lambda u: buffer(u.bytes) 告诉 sqlite3 如何将它们转换为 buffer(在 SQLite 中转换为 X'ABCD....' blob。
  • fectchone() 直接返回 uuid.UUID 对象。转换器lambda u: buffer(u.bytes) 告诉 sqlite3 在遇到声明的GUID 类型时如何从字节数组中创建它们。
  • 这些 GUID 以 16 字节二进制 blob 的形式存储,以小端序排列。
  • 我可以使用 SQLite Expert 成功打开/编辑数据库(在其默认配置中)。

【讨论】:

    【解决方案2】:

    您的 GUID 是一个字符串。将其声明为 VARCHAR 或 CHAR,它将正确获取文本关联。由于 GUID 不是类型,因此该字段没有类型关联,因此将具有 NONE 数据类型。

    这里有描述:http://www.sqlite.org/datatype3.html

    在他们的支持论坛上,还有一个关于有人试图在 SQLite Expert 中使用 Guid 的简短讨论:http://sqliteexpert.com/forum/YaBB.pl?num=1368018774/0

    支持讨论映射系统和如何更改映射,以及默认使用 Blob 的工具。

    【讨论】:

    • 那么,由于我的 GUID 数据类型声明,我所看到的只是 SQLite Expert 的一种奇怪行为吗?
    • 看起来像 - 我刚刚下载并查看了。如果您使用 GUI 来定义字段,它会将“GUID”作为选项之一。
    • 是的,基本上,由于 GUID 是一种未知类型,SQLite 无法确定它的类型亲和性。
    • 但是 someone 正在解析 ASCII GUID,并将其存储为 16 字节二进制 blob。是 SQLite Expert 还是 SQLite 库?
    • SQLite Expert 通过工具内部的映射系统实现其类型。通常这种类型的东西被称为“域”,您可以在其中为数据类型创建自己的名称。它们的 GUID 类型映射到 SQLite 专家内部的 blob,但您可以在工具中更改映射。
    猜你喜欢
    • 2013-04-14
    • 2017-11-02
    • 1970-01-01
    • 1970-01-01
    • 2018-11-17
    • 1970-01-01
    • 2013-05-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多