小巧。快速。可靠。
三选二。
创建表

1. 语法

create-table-stmt

CREATE TEMP TEMPORARY TABLE IF NOT EXISTS schema-name . table-name ( column-def table-constraint , ) table-options , AS select-stmt

column-def

select-stmt

table-constraint

table-options

2. CREATE TABLE 命令

"CREATE TABLE" 命令用于在 SQLite 数据库中创建一个新表。CREATE TABLE 命令指定新表的以下属性

每个 CREATE TABLE 语句都必须为新表指定一个名称。以“sqlite_”开头的表名保留供内部使用。尝试创建名称以“sqlite_”开头的表是错误的。

如果指定了 schema-name,则它必须是“main”、“temp”或 附加数据库 的名称。在这种情况下,新表将在指定的数据库中创建。如果“TEMP”或“TEMPORARY”关键字出现在“CREATE”和“TABLE”之间,则新表将在临时数据库中创建。指定 schema-name 和 TEMP 或 TEMPORARY 关键字是错误的,除非 schema-name 为“temp”。如果未指定模式名称且不存在 TEMP 关键字,则表将在主数据库中创建。

通常,尝试在数据库中创建新表是错误的,而该数据库已包含同名的表、索引或视图。但是,如果“IF NOT EXISTS”子句作为 CREATE TABLE 语句的一部分指定,并且同名的表或视图已存在,则 CREATE TABLE 命令将没有任何效果(并且不会返回错误消息)。如果由于现有索引而无法创建表,则仍会返回错误,即使指定了“IF NOT EXISTS”子句。

创建与现有 触发器 同名的表不是错误。

表使用 DROP TABLE 语句删除。

2.1. CREATE TABLE ... AS SELECT 语句

"CREATE TABLE ... AS SELECT" 语句根据 SELECT 语句的结果创建并填充数据库表。该表与 SELECT 语句返回的列数相同。每列的名称与 SELECT 语句结果集中相应列的名称相同。每列的声明类型由 SELECT 语句结果集中相应表达式的 表达式关联性 确定,如下所示

表达式关联性列声明类型
TEXT"TEXT"
NUMERIC"NUM"
INTEGER"INT"
REAL"REAL"
BLOB(也称为“NONE”)""(空字符串)

使用 CREATE TABLE AS 创建的表没有 PRIMARY KEY 也没有任何类型的约束。每列的默认值为 NULL。新表每列的默认排序规则为 BINARY。

使用 CREATE TABLE AS 创建的表最初会填充 SELECT 语句返回的数据行。行被分配连续递增的 rowid 值,从 1 开始,按照 SELECT 语句返回它们的 顺序

3. 列定义

除非是 CREATE TABLE ... AS SELECT 语句,否则 CREATE TABLE 包含一个或多个 列定义,之后可选地跟随着 表约束 列表。每个列定义都包含列的名称,之后可选地跟随着列的声明类型,然后是一个或多个可选的 列约束。出于先前语句的目的,在“列约束”的定义中包含 COLLATE 和 DEFAULT 子句,即使这些在某种意义上并不是真正的约束,因为它们不会限制表可能包含的数据。其他约束 - NOT NULL、CHECK、UNIQUE、PRIMARY KEY 和 FOREIGN KEY 约束 - 对表数据施加限制。

表的列数受 SQLITE_MAX_COLUMN 编译时参数限制。表的单行不能存储超过 SQLITE_MAX_LENGTH 字节的数据。这两个限制都可以在运行时使用 sqlite3_limit() C/C++ 接口降低。

3.1. 列数据类型

与大多数 SQL 数据库不同,SQLite 不会根据列的声明类型来限制可以插入列中的数据类型。相反,SQLite 使用 动态类型。列的声明类型仅用于确定列的 关联性

3.2. DEFAULT 子句

DEFAULT 子句指定一个默认值,如果用户在执行 INSERT 时未显式提供值,则使用该默认值。如果列定义没有附加显式 DEFAULT 子句,则该列的默认值为 NULL。显式 DEFAULT 子句可以指定默认值为 NULL、字符串常量、blob 常量、带符号数字或括号内包含的任何常量表达式。默认值也可以是以下特殊的不区分大小写的关键字之一:CURRENT_TIME、CURRENT_DATE 或 CURRENT_TIMESTAMP。出于 DEFAULT 子句的目的,如果表达式不包含子查询、列或表引用、绑定参数 或用双引号而不是单引号括起来的字符串文字,则认为该表达式是常量。

每次通过未为所有表列提供显式值的 INSERT 语句将行插入表时,新行中存储的值将由其默认值确定,如下所示

3.3. COLLATE 子句

COLLATE 子句指定排序规则的名称,该排序规则用作列的默认排序规则。如果未指定 COLLATE 子句,则默认排序规则为 BINARY

3.4. GENERATED ALWAYS AS 子句

包含 GENERATED ALWAYS AS 子句的列是 生成列。从 SQLite 版本 3.31.0(2020-01-22)开始支持生成列。有关生成列的功能和限制的详细信息,请参阅 单独的文档

3.5. PRIMARY KEY

SQLite 中的每个表最多只能有一个 PRIMARY KEY。如果将 PRIMARY KEY 关键字添加到列定义中,则表的 PRIMARY KEY 由该单列组成。或者,如果 PRIMARY KEY 子句作为 table-constraint 指定,则表的 PRIMARY KEY 由 PRIMARY KEY 子句中指定的列列表组成。PRIMARY KEY 子句必须仅包含列名——在 PRIMARY KEY 的 indexed-column 中使用表达式不受支持。如果在 CREATE TABLE 语句中出现多个 PRIMARY KEY 子句,则会引发错误。对于普通表,PRIMARY KEY 是可选的,但对于 WITHOUT ROWID 表是必需的。

如果表具有单列主键,并且该列的声明类型为“INTEGER”,并且该表不是 WITHOUT ROWID 表,则该列称为 INTEGER PRIMARY KEY。有关与 INTEGER PRIMARY KEY 相关联的特殊属性和行为的说明,请参阅 下文

具有主键的表中的每一行在其主键列中都必须具有唯一的组合值。出于确定主键值唯一性的目的,NULL 值被认为与所有其他值(包括其他 NULL)不同。如果 INSERTUPDATE 语句尝试修改表内容,以便两行或多行具有相同的主键值,则这是约束违规。

根据 SQL 标准,PRIMARY KEY 应始终暗示 NOT NULL。不幸的是,由于某些早期版本中的错误,在 SQLite 中并非如此。除非列是 INTEGER PRIMARY KEY 或表是 WITHOUT ROWID 表或 STRICT 表或列声明为 NOT NULL,否则 SQLite 允许 PRIMARY KEY 列中的 NULL 值。可以修复 SQLite 以符合标准,但这可能会破坏旧版应用程序。因此,已决定仅记录 SQLite 允许大多数 PRIMARY KEY 列中的 NULL 值这一事实。

3.6. UNIQUE 约束

UNIQUE 约束类似于 PRIMARY KEY 约束,只是单个表可以具有任意数量的 UNIQUE 约束。对于表上的每个 UNIQUE 约束,每一行都必须在 UNIQUE 约束标识的列中包含唯一的组合值。出于 UNIQUE 约束的目的,NULL 值被认为与所有其他值(包括其他 NULL)不同。与 PRIMARY KEY 一样,UNIQUE table-constraint 子句必须仅包含列名——在 UNIQUE table-constraintindexed-column 中使用表达式不受支持。

在大多数情况下,UNIQUE 和 PRIMARY KEY 约束通过在数据库中创建唯一索引来实现。(例外情况是 INTEGER PRIMARY KEYWITHOUT ROWID 表上的 PRIMARY KEY。)因此,以下模式在逻辑上是等价的

  1. CREATE TABLE t1(a, b UNIQUE);

  2. CREATE TABLE t1(a, b PRIMARY KEY);

  3. CREATE TABLE t1(a, b);
    CREATE UNIQUE INDEX t1b ON t1(b);

3.7. CHECK 约束

CHECK 约束可以附加到列定义上,也可以指定为表约束。在实践中,这两者没有区别。每次将新行插入表中或更新现有行时,与每个 CHECK 约束关联的表达式都会被求值并转换为数值,其方式与 CAST 表达式 相同。如果结果为零(整数 0 或实数 0.0),则发生了约束违规。如果 CHECK 表达式求值为 NULL 或任何其他非零值,则不是约束违规。CHECK 约束的表达式不能包含子查询。

CHECK 约束仅在写入表时进行验证,而不是在读取时进行验证。此外,可以使用“PRAGMA ignore_check_constraints=ON;”语句暂时禁用 CHECK 约束的验证。因此,查询可能会产生违反 CHECK 约束的结果。

3.8. NOT NULL 约束

NOT NULL 约束只能附加到列定义上,不能指定为表约束。毫不奇怪,NOT NULL 约束规定关联的列不能包含 NULL 值。尝试在插入新行或更新现有行时将列值设置为 NULL 会导致约束违规。NOT NULL 约束在查询期间不会被验证,因此即使列被标记为 NOT NULL,如果数据库文件已损坏,对列的查询也可能会产生 NULL 值。

4. 约束执行

约束在 INSERTUPDATE 期间以及通过 PRAGMA integrity_checkPRAGMA quick_check 进行检查,有时还会通过 ALTER TABLE 进行检查。查询和 DELETE 语句通常不验证约束。因此,如果数据库文件已损坏(可能是由于外部程序在不通过 SQLite 库的情况下直接更改数据库文件导致的),则查询可能会返回违反约束的数据。例如

CREATE TABLE t1(x INT CHECK( x>3 ));
/* Insert a row with X less than 3 by directly writing into the
** database file using an external program */
PRAGMA integrity_check;  -- Reports row with x less than 3 as corrupt
INSERT INTO t1(x) VALUES(2);  -- Fails with SQLITE_CORRUPT
SELECT x FROM t1;  -- Returns an integer less than 3 in spite of the CHECK constraint

可以使用 PRAGMA ignore_check_constraints=ON; 语句暂时禁用 CHECK 约束的执行。

4.1. 对约束违规的响应

对约束违规的响应由 约束冲突解决算法 决定。每个 PRIMARY KEY、UNIQUE、NOT NULL 和 CHECK 约束都有一个默认的冲突解决算法。PRIMARY KEY、UNIQUE 和 NOT NULL 约束可以通过在其定义中包含 冲突子句 来显式分配另一个默认的冲突解决算法。或者,如果约束定义不包含 冲突子句,则默认的冲突解决算法为 ABORT。CHECK 约束的冲突解决算法始终为 ABORT。(出于向后兼容性的原因,表 CHECK 约束允许具有冲突解决子句,但这没有任何作用。)同一表中的不同约束可能具有不同的默认冲突解决算法。有关更多信息,请参阅标题为 ON CONFLICT 的部分。

5. ROWID 和 INTEGER PRIMARY KEY

除了 WITHOUT ROWID 表之外,SQLite 表中的所有行都具有一个 64 位有符号整数键,该键唯一标识表中的行。此整数通常称为“rowid”。可以使用特殊的不区分大小写的名称“rowid”、“oid”或“_rowid_”代替列名来访问 rowid 值。如果表包含名为“rowid”、“oid”或“_rowid_”的用户定义列,则该名称始终引用显式声明的列,不能用于检索整数 rowid 值。

WITHOUT ROWID 表中,rowid(以及“oid”和“_rowid_”)被省略。WITHOUT ROWID 表仅在 SQLite 版本 3.8.2(2013-12-06)及更高版本中可用。缺少 WITHOUT ROWID 子句的表称为“rowid 表”。

rowid 表的数据存储为 B 树结构,其中包含表中每一行的条目,并使用 rowid 值作为键。这意味着通过 rowid 检索或排序记录的速度很快。搜索具有特定 rowid 的记录或搜索具有指定范围内的 rowid 的所有记录的速度大约是通过指定任何其他 PRIMARY KEY 或索引值进行的类似搜索的两倍。

除了下面提到的一个例外情况外,如果 rowid 表的主键由单个列组成,并且该列的声明类型为“INTEGER”(不区分大小写),则该列将成为 rowid 的别名。这样的列通常称为“整数主键”。只有当声明的类型名称完全为“INTEGER”时,PRIMARY KEY 列才会成为整数主键。其他整数类型名称(如“INT”或“BIGINT”或“SHORT INTEGER”或“UNSIGNED INTEGER”)会导致主键列表现为具有整数 亲和性 和唯一索引的普通表列,而不是 rowid 的别名。

上面提到的例外情况是,如果声明类型为“INTEGER”的列的声明包含“PRIMARY KEY DESC”子句,则它不会成为 rowid 的别名,并且不会被归类为整数主键。这种怪异行为并非设计使然。这是由于早期版本的 SQLite 中存在错误。但是,修复该错误可能会导致向后不兼容。因此,保留了原始行为(并进行了记录),因为角落情况下的奇特行为远比兼容性中断要好。这意味着以下三个表声明都会导致列“x”成为 rowid 的别名(整数主键)

但以下声明不会导致“x”成为 rowid 的别名

可以使用 UPDATE 语句以与任何其他列值相同的方式修改 Rowid 值,可以使用内置别名之一(“rowid”、“oid”或“_rowid_”)或使用整数主键创建的别名。类似地,INSERT 语句可以提供一个值作为插入的每一行的 rowid。与普通的 SQLite 列不同,整数主键或 rowid 列必须包含整数。整数主键或 rowid 列无法保存浮点数、字符串、BLOB 或 NULL。

如果 UPDATE 语句尝试将整数主键或 rowid 列设置为 NULL 或 blob 值,或者设置为无法无损转换为整数的字符串或实数值,则会发生“数据类型不匹配”错误,并且语句将被中止。如果 INSERT 语句尝试将 blob 值或无法无损转换为整数的字符串或实数值插入整数主键或 rowid 列中,则会发生“数据类型不匹配”错误,并且语句将被中止。

如果 INSERT 语句尝试将 NULL 值插入 rowid 或整数主键列,则系统会自动选择一个整数作为 rowid。如何执行此操作的详细说明在 单独 提供。

父键外键约束 不允许使用 rowid。父键必须仅使用命名的列。

此页面上次修改于 2024-03-13 17:43:35 UTC