在SQLite表中使用文本作为主键是否不好?

41

在SQLite数据库中,使用文本作为主键是否会影响性能?听说这样做会降低性能,这是真的吗?如果这样做,rowid是否会被用作实际的主键?

7个回答

50

在SQLite数据库中,将文本作为主键是否不好?我听说这会影响性能,这是真的吗?

从正确性的角度来看,TEXT PRIMARY KEY 是可以的。

从性能的角度来看,最好使用 INTEGER 键。但是对于任何性能问题,都需要根据自己的数据和使用情况进行测量,以查看是否存在显着差异。

那么在这种情况下,ROWID 会被用作实际的主键吗?

只有 INTEGER PRIMARY KEYROWID 别名相同。其他类型的主键不会,除非指定了 WITHOUT ROWID参考资料


3
那么隐式rowid的用途是什么? - mpellegr
为了明确起见,您能明确说明是引用隐式rowid还是TEXT主键吗? - dobbs
如果您不使用 WITHOUT ROWID,SQlite将使用行ID作为表的主搜索树的键,并创建一个单独的搜索树来将文本主键与其行ID关联起来(因为任何主键列都会获得唯一索引)。因此,对于具有文本主键的表来说,通常使用WITHOUT ROWID是个好主意。我认为整数与字符串比较速度在几乎任何SQLite表中并不重要,因为主键列上有索引。(有关更多详细信息,请参阅下面我的回答。) - Zoë Sparks

37

在现实世界中,如果我们使用UUID作为主键,那么使用字符串作为主键有很多好处。能够在实体"护照"创建的时刻恰好创建实体可以大大简化异步代码和/或分布式系统(如果我们谈论更复杂的移动客户端/服务器架构)。

至于性能方面,在运行10000个主键查找的基准测试时,我没有发现任何可衡量的差异,因为在运行索引搜索时,数据库索引既不存储也不比较字符串。


1
我认为UUID由有限数量的符号(VARCHAR)组成。因此,它有点像多字节整数,您可以将其视为char、word、int、long等的扩展...而TEXT具有可变长度,并且是另一回事。 - Nikolay
1
@Nikolay:请注意,与其他SQL引擎不同,SQLite仅具有用于存储文本数据的单个类型:TEXT。例如VARCHAR或CHARACTER等名称是TEXT的同义词。如果您指定了最大长度,例如VARCHAR(20),则此限制将被静默忽略。 - ramirami

3
尽管此线程讨论整数与文本主键,但为了背景,请参见2021年左右SQLite创始人Richard Hipp在Blob vs. Text for primary keys中的回复。我已经复制并强调了他回复中相关的部分。

(2)Richard Hipp(drh)于2021-03-04 16:00:22回复1 [source]

这两种方法都应该可以正常工作。将哈希值存储为BLOB可能会非常稍微快一些,因为(如您所观察到的)内容较少,因此文件I/O较少。

类似于此的是Fossil版本控制系统。但它将哈希值存储为文本而不是blob。性能不是问题,而且文本在开发人员调试时更容易处理。


1
通常来说,在 SO(Stack Overflow)的帖子中至少总结一下链接是最佳实践。这是很棒的信息 - 谢谢! - sage

0

使用文本主键在本质上没有问题。主键的有效性在于它是可排序的,并且在表中具有唯一值;除此之外,数据类型并不严格要求。然而,当数据是文本时,通常来自“现实世界”的数据源会排除将其用作主键的实用性。使用任意的、无意义的整数作为主键意味着您不必担心这样的问题。

这可能是整数主键最好的地方,甚至可以说比整数与字符串比较的速度更快。与整数比较相比,字符串比较通常对计算机而言需要更多的工作,确实如此,但在这种情况下这可能并不重要。SQLite在任何表中都为主键创建索引,这意味着即使您有百万条记录,SQLite在最坏的情况下只需要执行大约13次比较就能找到行(O(log n))。除非遇到极端情况,否则这不太可能对性能产生重大影响,我想。

顺便提一下,如果你打算使用文本作为主键,你可能要考虑使用SQLite的WITHOUT ROWID功能。一个带有文本主键的表不太可能需要rowid列,因为rowid本质上是一个整数主键。WITHOUT ROWID不仅可以消除rowid列,还告诉SQLite将表本身的搜索树基于你指定的主键而不是rowid。否则,它会创建两个搜索树,一个用于表本身的主搜索树使用rowid键,另一个用于将文本主键与rowid关联的独立搜索树。这浪费空间并增加了使用文本主键进行查找时不必要的开销,假设你不需要rowid。

SQLite的WITHOUT ROWID文档解释了所有这些内容。他们给出了一个在文本语料库中存储单词计数的表的示例,其中单词作为主键,这对我来说是一个很好的文本主键适用的情况的例子。


-2

是的,如果您使用TEXT,则会出现以下错误: android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: TableName.ColumnName (code 1555)

如果插入成功,SQLite会话将插入并返回最后一行插入的行ID。否则将返回-1。

返回映射到_ID,这就是它们强制您为表接口BaseColumns的原因。

很奇怪插入调用必须返回rowid,而不是布尔值或其他内容

我希望SQLite中有TEXT PRIMARY KEY功能


-4

主键类型的字段意味着比较值。比较数字比比较文本更简单。

原因是64位数字比较有特定的汇编指令。这将始终比比较理论上可以无限大小的文本快得多。

比较数字的示例:

CMP DX, 00  ; Compare the DX value with zero
JE  L7      ; If yes, then jump to label L7
.
.
L7: ...

关于 CMP 汇编指令的更多阅读,请点击https://www.tutorialspoint.com/assembly_programming/assembly_conditions.htm

了解这一点让我们知道数字在计算机性能方面总是更为出色的(至少在目前的计算中)。


-44
在SQLite数据库中把文本作为主键是不好的吗?我听说这会有性能问题,这是真的吗?
我从来没有听说过有人在表格中使用字符串作为主键。对我来说(我真诚地希望其他人也是这样),这是一种非常丑陋的做法,效率很低。
如果您将字符串用作主键,您需要考虑以下几个问题:
- 三个符号的组合是否足够? - 还是应该使用五个符号?
在这里,每行必须具有相同的格式(当然是可读性问题),并且还必须是唯一的。哦!这里是下一个"繁琐的工作"-> 您需要创建一些"唯一字符串生成器",它将生成唯一的1个字符串标识符2。
此外,还有其他问题需要考虑:
  • 越长的字符串,比较起来就越难
  • 表的大小会急剧增加,因为很明显字符串的大小比数字大得多
  • 行数 - 如果您的表可以有1000+行,使用字符串作为主键就是疯狂的

这是一个更复杂的主题,但我想说,对于非常小的表,如果有意义的话,可能可以使用字符串作为主键,但是如果您看一下缺点,使用数字作为主键肯定是更好的技术!

那结论呢?

我不建议您使用字符串作为主键。 它的缺点比优点多(它真的有一些优点吗?)。

使用数字作为主键是更好的选择(我害怕说是最佳实践)。

在这种情况下,rowid会被用作实际的主键吗?

如果您将字符串用作主键,则不会。

1实际上,字符串很少是唯一的。

2当然,你可以说你可以从行中的项目名称创建标识符,但这又是一种意大利面式代码(项目可能具有相同的名称)。


4
我的字符串来自服务器,保证是唯一的,并且始终具有固定的长度,这个事实是否改变了答案? - mpellegr
7
你能否测量与文本主键相关的“非常低的性能”? 因为此问题的答案(与当前问题非常相似,除了SQLite部分)没有提到这样的问题。 - Faibbus
1
所有的观点都是好的和有效的,但有一种边缘情况,其中一个递增整数只会引起问题,那就是当您需要在多个设备和主数据存储之间保持数据同步时。GUID足够唯一。 - LordWabbit
1
我没看到有人提供任何真正的理由说明这是不好的做法。如果您将UUID作为表行的主要标识符,请将其用作主键。 - Gregory Ray
1
看一下一个叫做CouchDB的数据库,它是一个面向文档的数据库,文档ID(主键)总是保存为文本。 - Tiago Stapenhorst
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接