非常教程

Sqlite参考手册

其他 | Miscellaneous

Clustered Indexes and the WITHOUT ROWID Optimization

默认情况下,SQLite中的每一行都有一个特殊的列,通常称为“rowid”,用于唯一标识表中的行。但是,如果将短语“WITHOUT ROWID”添加到CREATE TABLE语句的末尾,则省略特殊的“rowid”列。省略rowid有时有空间和性能方面的优势。

WITHOUT ROWID表是使用“ 集群索引”作为主键的表。

1.1.句法

要创建WITHOUT ROWID表,只需将关键字“WITHOUT ROWID”添加到CREATE TABLE语句的末尾即可。例如:

CREATE TABLE IF NOT EXISTS wordcount(
  word TEXT PRIMARY KEY,
  cnt INTEGER
) WITHOUT ROWID;

与所有SQL语法一样,关键字的情况并不重要。人们可以写出“WITHOUT rowid”或“没有rowid”或“WiThOuT rOwId”,它意味着同样的事情。

每个WITHOUT ROWID表都必须有一个PRIMARY KEY。如果带有WITHOUT ROWID子句的CREATE TABLE语句缺少PRIMARY KEY,则会引发错误。

在大多数情况下,普通表的特殊“rowid”列也可以称为“oid”或“_rowid_”。但是,只有“rowid”作为CREATE TABLE语句中的关键字。

1.2.兼容性

需要SQLite 版本3.8.2(2013-12-06)或更高版本才能使用WITHOUT ROWID表。尝试使用早期版本的SQLite打开包含一个或多个WITHOUT ROWID表的数据库将导致“格式错误的数据库模式”错误。

1.3.怪异模式

据我们所知,WITHOUT ROWID仅在SQLite中找到,并且与任何其他SQL数据库引擎都不兼容。在一个优雅的系统中,即使没有WITHOUT ROWID关键字,所有表的表现也会与WITHOUT ROWID表相同。但是,当首次设计SQLite时,它仅使用整数rowid作为行键以简化实现。这种方法多年来运作良好。但随着对SQLite需求的增长,PRIMARY KEY确实与底层行密钥对应的表的需求变得更加尖锐。WITHOUT ROWID概念是为了满足这种需求而添加的,而不会破坏当时已经使用的数十亿SQLite数据库(大约2013年)的向后兼容性。

WITHOUT ROWID语法是一种优化。它不提供新功能。任何可以使用WITHOUT ROWID表完成的事情也可以用完全相同的方式完成,并且使用普通的rowid表完全相同的语法。WITHOUT ROWID表的唯一优点是,它有时可以使用较少的磁盘空间和/或执行比普通的rowid表快一点。

大多数情况下,普通的rowid表和WITHOUT ROWID表是可以互换的。但是,对于不适用于普通rowid表的WITHOUT ROWID表有一些额外的限制:

  1. 每个WITHOUT ROWID表都必须有一个PRIMARY KEY。尝试创建没有PRIMARY KEY的WITHOUT ROWID表会导致错误。
  1. 与“INTEGER PRIMARY KEY”关联的特殊行为不适用于WITHOUT ROWID表。在普通表中,“INTEGER PRIMARY KEY”表示该列是rowid的别名。但是由于在WITHOUT ROWID表中没有rowid,所以该特殊含义不再适用。WITHOUT ROWID表中的“INTEGER PRIMARY KEY”列与普通表中的“INT PRIMARY KEY”列类似:它是具有整数亲和性的PRIMARY KEY。
  1. AUTOINCREMENT 在WITHOUT ROWID表格上不起作用。AUTOINCREMENT机制假定存在一个rowid,所以它在WITHOUT ROWID表中不起作用。如果在WITHOUT ROWID表的CREATE TABLE语句中使用了“AUTOINCREMENT”关键字,则会出现错误。
  1. NOT NULL在WITHOUT ROWID表中的PRIMARY KEY的每一列上执行。这符合SQL标准。PRIMARY KEY的每列应该是单独的NOT NULL。但是,由于错误,早期版本的SQLite并未在PRIMARY KEY列上强制执行NOT NULL。在发现这个错误的时候,已经有很多SQLite数据库已经在发布,决定不去修复这个bug,因为害怕破坏兼容性。所以,SQLite中的普通rowid表违反了SQL标准,并允许PRIMARY KEY字段中的NULL值。但WITHOUT ROWID表遵循标准,并会在尝试将NULL插入PRIMARY KEY列时引发错误。
  1. sqlite3_last_insert_rowid() 函数不会对没有ROWID表工作。插入WITHOUT ROWID不会更改sqlite3_last_insert_rowid()函数返回的值。last_insert_rowid()SQL函数也不受影响,因为它只是sqlite3_last_insert_rowid()的一个包装。
  1. 增量BLOB I / O 机制不能对于没有ROWID表工作。增量BLOB I / O使用rowid为执行直接I / O创建sqlite3_blob对象。但是,WITHOUT ROWID表没有rowid,因此无法为WITHOUT ROWID表创建sqlite3_blob对象。
  1. sqlite3_update_hook() 接口不火对于没有ROWID表变为回调。sqlite3_update_hook()的部分回调是已更改的表行的rowid。但是,没有ROWID表没有rowid。因此,当WITHOUT ROWID表更改时,不会调用更新挂钩。

WITHOUT ROWID表是可以减少存储和处理要求的优化。

在普通的SQLite表中,PRIMARY KEY实际上只是一个UNIQUE索引。用于在磁盘上查找记录的键是rowid。普通SQLite表中的特殊“INTEGER PRIMARY KEY”列类型使该列成为rowid的别名,因此INTEGER PRIMARY KEY是真正的PRIMARY KEY。但是任何其他类型的PRIMARY KEY,包括“INT PRIMARY KEY”,都只是普通rowid表中的唯一索引。

考虑一个表格(如下所示),用于存储单词词汇以及某些文本语料库中每个单词的出现次数。

CREATE TABLE IF NOT EXISTS wordcount(
  word TEXT PRIMARY KEY,
  cnt INTEGER
);

作为一个普通的SQLite表,“wordcount”是作为两个单独的B-树实现的。主表使用隐藏的rowid值作为键,并将“word”和“cnt”列存储为数据。CREATE TABLE语句的“TEXT PRIMARY KEY”短语会导致在“单词”列上创建唯一索引。该索引是一个单独的B树,它使用“word”和“rowid”作为关键字,并且根本不存储数据。请注意,每个“单词”的完整文本都存储了两次:一次在主表中,另一次在索引中。

考虑查询此表以找出单词“xyzzy”的出现次数。

SELECT cnt FROM wordcount WHERE word='xyzzy';

此查询首先必须搜索索引B-Tree,查找包含“word”的匹配值的任何条目。在索引中找到条目时,rowid被提取并用于搜索主表。然后从主表中读出“cnt”值并返回。因此,需要两个独立的二进制搜索来完成请求。

WITHOUT ROWID表为同等表使用不同的数据设计。

CREATE TABLE IF NOT EXISTS wordcount(
  word TEXT PRIMARY KEY,
  cnt INTEGER
) WITHOUT ROWID;

在后面的表格中,只有一个B-Tree使用“word”列作为键,而“cnt”列作为其数据。(技术性:低级实现实际上在B-Tree的“key”区域中存储“word”和“cnt”,但除非您正在查看数据库文件的低级字节编码,否则不重要)。因为只有一个B-Tree,所以“字”列的文本只在数据库中存储一次。此外,查询特定“单词”的“cnt”值仅涉及在主B-Tree中的单个二进制搜索,因为可以直接从该第一搜索找到的记录中检索“cnt”值,而不需要在rowid上进行第二次二元搜索。

因此,在某些情况下,WITHOUT ROWID表可以使用大约一半的磁盘空间,并且可以以近两倍的速度运行。当然,在现实世界的模式中,通常会有二级索引和/或UNIQUE约束,情况会更复杂。但即使如此,在具有非整数或复合PRIMARY KEY的表上使用WITHOUT ROWID通常也可能具有空间和性能优势。

WITHOUT ROWID优化对于具有非整数或复合(多列)PRIMARY KEY并且不存储大型字符串或BLOB的表很可能会有所帮助。

WITHOUT ROWID表将正确工作(也就是说,它们提供了正确的答案),使用单个INTEGER PRIMARY KEY表。但是,在这种情况下,普通的rowid表将运行得更快。因此,避免使用INTEGER类型的单列PRIMARY KEY创建WITHOUT ROWID表是很好的设计。

WITHOUT ROWID当单个行不太大时,表格的效果最好。一个好的经验法则是,WITHOUT ROWID表中的单个行的平均大小应该小于数据库页面大小的大约1/20。这意味着,对于1KiB页面大小,每行不应超过约50个字节,对于4KiB页面大小,每行不应超过约200个字节。没有ROWID的表对于任意大的行都会起作用(在他们得到正确答案的意义上) - 最大2GB - 但传统的rowid表往往对于较大的行大小工作得更快。这是因为rowid表被实现为B * -Trees,其中所有内容都存储在树的叶子中,而WITHOUT ROWID表则使用普通的B树来实现,其内容存储在树叶和中间节点上。

“sqlite3_analyzer.exe”实用程序可用作SQLite源代码树中的源代码或SQLite下载页面上的预编译二进制文件,用于测量现有SQLite数据库中表格行的平均大小。

请注意,除了上面详细描述的几个角落差异之外,WITHOUT ROWID表和rowid表的工作原理是相同的。在给定相同的SQL语句的情况下,它们都生成相同的答案 因此,在开发周期的后期对应用程序运行实验是一件简单的事情,以测试使用WITHOUT ROWID表是否有帮助。一个好的策略是在产品开发接近尾声的时候不用担心WITHOUT ROWID,然后返回并运行测试,以确定将WITHOUT ROWID添加到具有非整数PRIMARY KEY的表是否有助于或损害性能,并仅保留WITHOUT ROWID在那些有帮助的情况下。

 SQLite在公共领域。

其他 | Miscellaneous相关

1.35% Faster Than The Filesystem
2.8+3 Filenames
3.An Asynchronous I/O Module For SQLite
4.Appropriate Uses For SQLite
5.Architecture of SQLite
6.Atomic Commit In SQLite
7.Automatic Undo/Redo With SQLite
8.Benefits of SQLite As A File Format
9.Change in Default Page Size in SQLite Version 3.12.0
10.Compile-time Options
11.Constraint Conflict Resolution in SQLite
12.Custom Builds Of SQLite
13.Deterministic SQL Functions
14.Distinctive Features Of SQLite
15.EXPLAIN QUERY PLAN
16.Features Of SQLite
17.File Format Changes in SQLite
18.Full-Featured SQL
19.High Reliability
20.Hints for Debugging SQLite
21.How SQLite Is Tested
22.How To Compile SQLite
23.How To Download Canonical SQLite Source Code
24.Imposter Tables
25.In-Memory Databases
26.Indexes On Expressions
27.Internal Versus External BLOBs
28.Isolation In SQLite
29.Long Term Support
30.Maintaining Private Branches Of SQLite
31.Many Small Queries Are Efficient In SQLite
32.Measuring and Reducing CPU Usage in SQLite
33.Memory-Mapped I/O
34.NULL Handling in SQLite
35.Partial Indexes
36.Pointer Passing Interfaces
37.Powersafe Overwrite
38.Release History Of SQLite
39.Result and Error Codes
40.Row Values
41.Rowid Tables
42.Run-Time Loadable Extensions
43.SQL Features That SQLite Does Not Implement
44.sqldiff.exe: Database Difference Utility
45.SQLite As An Application File Format
46.SQLite Autoincrement
47.SQLite Backup API
48.SQLite Changes From Version 3.4.2 To 3.5.0
49.SQLite Changes From Version 3.5.9 To 3.6.0
50.SQLite Database Speed Comparison
51.SQLite File IO Specification
52.SQLite Frequently Asked Questions
53.SQLite In 5 Minutes Or Less
54.SQLite is a Self Contained System
55.SQLite Is Serverless
56.SQLite Is Transactional
57.SQLite Library Footprint
58.SQLite Shared-Cache Mode
59.SQLite Unlock-Notify API
60.SQLite Version 3 Overview
61.SQLite: Single File Database
62.Temporary Files Used By SQLite
63.TH3
64.The COMPLETION() Table-Valued Function
65.The CSV Virtual Table
66.The dbhash.exe Utility Program
67.The DBSTAT Virtual Table
68.The Error And Warning Log
69.The generate_series Table-Valued Function
70.The OS Backend (VFS) To SQLite
71.The Spellfix1 Virtual Table
72.The SQLite Amalgamation
73.The SQLite Bytecode Engine
74.The sqlite3_analyzer.exe Utility Program
75.The SQLITE_STMT Virtual Table
76.The UNION Virtual Table
77.The Virtual Database Engine of SQLite
78.Uniform Resource Identifiers
79.Using SQLite In Multi-Threaded Applications
80.Version Numbers in SQLite
81.What If OpenDocument Used SQLite?
82.Why Is SQLite Coded In C
83.Zero-Configuration
Sqlite

SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如 Tcl、C#、PHP、Java等,还有ODBC接口,同样比起Mysql、PostgreSQL这两款开源的世界著名数据库管理系统来

主页 https://sqlite.org/
源码 https://www.sqlite.org/src/
发布版本 3.21.0

Sqlite目录

1.C界面 | C Interface
2.C Interface: Session Module
3.CLI
4.数据库文件表 | Database File Format
5.数据类 | Datatypes
6.动态内存分配 | Dynamic Memory Allocation
7.外键约束 | Foreign Key Constraints
8.全文索引 | Full-Text Search
9.损坏方式 | How To Corrupt
10.JSON
11.语言 | Language
12.局限性 | Limits
13.锁定和并发 | Locking and Concurrency
14.其他 | Miscellaneous
15.PRAGMA Statements
16.查询计划程序 | Query Planner
17.R*Tree Module
18.RBU Extension
19.语法图 | Syntax Diagrams
20.Tcl Interface
21.虚拟表机制 | Virtual Table Mechanism
22.预写日志 | Write-Ahead Logging
23.SQL 教程
24.SQL 简介
25.SQL 语法
26.SQL DELETE 语句
27.SQL UPDATE 语句
28.SQL NOT NULL 约束
29.SQL 约束
30.SQL CREATE TABLE 语句
31.SQL CREATE DATABASE 语句
32.SQL INSERT INTO SELECT 语句
33.SQL SELECT INTO 语句
34.SQL CREATE VIEW、REPLACE VIEW、 DROP VIEW 语句
35.SQL AUTO INCREMENT 字段
36.SQL ALTER TABLE 语句
37.SQL 撤销索引、表以及数据库
38.SQL CREATE INDEX 语句
39.SQL DEFAULT 约束
40.SQL CHECK 约束
41.SQL FOREIGN KEY 约束
42.SQL PRIMARY KEY 约束
43.SQL UNIQUE 约束
44.SQL 通用数据类型
45.SQL ISNULL()、NVL()、IFNULL() 和 COALESCE() 函数
46.SQL NULL 值 – IS NULL 和 IS NOT NULL
47.SQL Server 和 MySQL 中的 Date 函数
48.SQL MS Access、MySQL 和 SQL Server 数据类型
49.SQL 函数
50.SQL 总结
51.SQL 主机
52.SQL 快速参考
53.SQL ROUND() 函数
54.SQL Server GETDATE() 函数
55.MySQL DATE_FORMAT() 函数
56.MySQL DATEDIFF() 函数
57.MySQL DATE_SUB() 函数
58.MySQL DATE_ADD() 函数
59.MySQL EXTRACT() 函数
60.MySQL DATE() 函数
61.MySQL CURTIME() 函数
62.MySQL CURDATE() 函数
63.MySQL NOW() 函数
64.SQL Server CONVERT() 函数
65.SQL Server DATEDIFF() 函数
66.SQL Server DATEADD() 函数
67.SQL Server DATEPART() 函数
68.SQLite 命令
69.SQLite 安装
70.SQLite 简介
71.SQLite 运算符
72.SQLite Select 语句
73.SQLite 删除表
74.SQLite 创建表
75.SQLite Insert 语句
76.SQLite 分离数据库
77.SQLite 附加数据库
78.SQLite 创建数据库
79.SQLite 数据类型
80.SQLite 语法
81.SQLite Order By
82.SQLite Limit 子句
83.SQLite Glob 子句
84.SQLite Like 子句
85.SQLite Delete 语句
86.SQLite Update 语句
87.SQLite AND/OR 运算符
88.SQLite Where 子句
89.SQLite 表达式
90.SQLite Distinct 关键字
91.SQLite Having 子句
92.SQLite Group By
93.SQLite Join
94.SQLite 约束
95.SQLite PRAGMA
96.SQLite 事务
97.SQLite 视图
98.SQLite Truncate Table
99.SQLite Alter 命令
100.SQLite Indexed By