SQLite多列主键

746
在SQLite中,指定多个列的主键语法是什么?

22
这也被称为复合键 http://zh.wikipedia.org/wiki/%E5%A4%8D%E5%90%88%E9%94%AE - OneWorld
14
如果任何一列本身不是关键字,则为@OneWorld或复合关键字。 - Michael
9个回答

981
根据CREATE TABLE的文档,特别是table-constraint部分,它是这样的:
CREATE TABLE something (
  column1, 
  column2, 
  column3, 
  PRIMARY KEY (column1, column2)
);

9
根据文档,CREATE TABLE something (column1 PRIMARY KEY, column2 PRIMARY KEY); 应该也是可以的,但实际上不行。 - Yar
13
文档中写道:"如果在一个CREATE TABLE语句中有多个PRIMARY KEY子句,则会出错。"是的,铁路图可能也显示这是有效的,但下面的文本澄清了这一点。 - Brian Campbell
19
请记得在最后添加像这个答案中的 PRIMARY KEY(column1, column2) 部分。如果您尝试在 column2 定义之后添加它,您将会得到一个 语法错误 - vovahost
1
由于主键两种不同的定义方式:一种是在列定义中使用列约束,另一种是使用表约束。它们使用不同的语法,因此只有后者(在结尾处)允许列的列表。 - U. Windl

189
CREATE TABLE something (
  column1 INTEGER NOT NULL,
  column2 INTEGER NOT NULL,
  value,
  PRIMARY KEY ( column1, column2)
);

主键不是强制非空的吗? - pratnala
35
在标准的SQL中,是允许使用 NULL 作为主键的。但在SQLite中,NULL 也是允许作为主键的。这个答案强调了如果想要更加规范的行为,需要自己添加 NOT NULL。我的回答只是多列主键的基本语法。 - Brian Campbell

52

是的。但请记住,这样的主键允许在两个列中多次使用NULL值。

创建一个如下的表:

    sqlite> CREATE TABLE something (
column1, column2, value, PRIMARY KEY (column1, column2));

现在这个代码可以正常工作,没有任何警告:

sqlite> insert into something (value) VALUES ('bla-bla');
sqlite> insert into something (value) VALUES ('bla-bla');
sqlite> select * from something;
NULL|NULL|bla-bla
NULL|NULL|bla-bla

有关这种行为的原因有参考资料吗?如果数据库中包含重复项(即使它们包含“NULL”),有什么好的方法可以转储多行并删除它们? - Pastafarianist
6
根据SQL标准,主键应始终意味着非空(NOT NULL)。不幸的是,在SQLite的某些早期版本中由于一个错误,这并非如此。...空值被认为与所有其他值(包括其他空值)都不同。 - The incredible Jan
是的,在SQL中,NULL始终比较为假。因此,关系理论明确排除了NULL作为任何键组件的值。然而,SQLite是关系实践。似乎作者选择从实用角度允许多个但不“相等”的键。显然,最好不要允许将NULL作为键值。 - holdenweb
实际上这不是一个答案,而是对https://dev59.com/CnRB5IYBdhLWcg3wEDul#3685672评论的回复,该答案是https://dev59.com/CnRB5IYBdhLWcg3wEDul#734704的重复。管理员可能现在已经为时过晚。 - U. Windl

45

基本:

CREATE TABLE table1 (
    columnA INTEGER NOT NULL,
    columnB INTEGER NOT NULL,
    PRIMARY KEY (columnA, columnB)
);

如果您的列是其他表的外键(常见情况):

CREATE TABLE table1 (
    table2_id INTEGER NOT NULL,
    table3_id INTEGER NOT NULL,
    FOREIGN KEY (table2_id) REFERENCES table2(id),
    FOREIGN KEY (table3_id) REFERENCES table3(id),
    PRIMARY KEY (table2_id, table3_id)
);

CREATE TABLE table2 (
    id INTEGER NOT NULL,
    PRIMARY KEY id
);

CREATE TABLE table3 (
    id INTEGER NOT NULL,
    PRIMARY KEY id
);

17

主键字段应声明为 not null(这与主键的定义是唯一且不为空不符合标准)。但以下是在任何DBMS中对多列主键的良好实践。

create table foo
(
  fooint integer not null
  ,foobar string not null
  ,fooval real
  ,primary key (fooint, foobar)
)
;

真实的。SQLite呼叫出根据SQL标准,PRIMARY KEY应始终意味着NOT NULL。不幸的是,由于一些早期版本中的错误,这在SQLite中并非如此。[...]如果使用SQLite,则需要为所有主键列传递NOT NULL - legends2k

14
自 SQLite 3.8.2 版本以来,明确 NOT NULL 规范的一个替代方案是“WITHOUT ROWID”规范。[1]
NOT NULL is enforced on every column of the PRIMARY KEY
in a WITHOUT ROWID table.

"WITHOUT ROWID"(无行ID)表具有潜在的效率优势,因此可以考虑一个更简洁的选择:

CREATE TABLE t (
  c1, 
  c2, 
  c3, 
  PRIMARY KEY (c1, c2)
 ) WITHOUT ROWID;

例如,在sqlite3提示符下: sqlite> insert into t values(1,null,3); 错误: NOT NULL约束失败:t.c2

1
对于现在阅读此内容的任何人:WITHOUT ROWID具有额外的含义,不应作为在主键旁边写入NOT NULL的替代方法。 - shadowtalker
我总是为 PRIMARY KEY 指定 NOT NULL,无论是否使用 WITHOUT ROWID,因为它是明确的,不会随着默认值而改变,并且可以避免我记住 SQLite 的怪癖。相关评论在 Ken Reed 的回答中 - legends2k

7
以下代码在SQLite中创建了一个具有2列主键的表。

解决方案:

CREATE TABLE IF NOT EXISTS users (
    id TEXT NOT NULL, 
    name TEXT NOT NULL, 
    pet_name TEXT, 
    PRIMARY KEY (id, name)
)

3

2

PRIMARY KEY (id, name) 对我来说不起作用。相反,添加约束条件才行。

CREATE TABLE IF NOT EXISTS customer (
    id INTEGER, name TEXT,
    user INTEGER,
    CONSTRAINT PK_CUSTOMER PRIMARY KEY (user, id)
)

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