具有冲突策略的SQLite中的唯一列对约束

时间:2018-08-20 09:33:14

标签: sqlite

我需要为一行中的两列创建一个唯一约束,并附加冲突策略。假设我们有一张桌子:

CREATE TABLE `telephones`(
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
telephone STRING NOT NULL);

因此很显然,它是一个单独的表,用于显示用户与其电话之间的一对多关系。我需要为user_idtelephone创建一个唯一索引,因此数据库不应有重复项。 在AFAIK中,有两种创建此类约束的方法:通过将索引创建为单独的SQL请求或通过在CREATE TABLE语句内部创建约束。第一种方式如下:

CREATE UNIQUE INDEX `user_ids_and_telephones` ON `telephones`(`user_id`, `telephone`) ON CONFLICT IGNORE

第二种方式如下:

CREATE TABLE `telephones`(
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
telephone STRING NOT NULL,
UNIQUE(`user_id`, `telephone`) ON CONFLICT IGNORE);

我的问题是:这些方法是否等效,并且都可以正确地实现所描述的目标?或者它们是否存在一些逻辑差异,这些差异会影响后续重复插入的逻辑?

我没有发现文档很清晰。

1 个答案:

答案 0 :(得分:1)

这两种方式都会创建索引,因此它们以相同的方式起作用(请参见下文)。该文档指出为:-

  

在大多数情况下,UNIQUE和PRIMARY KEY约束是通过以下方式实现的:   在数据库中创建唯一索引。 (例外是整数   没有ROWID表上的PRIMARY KEY和PRIMARY KEY。)因此,   以下架构在逻辑上是等效的:

CREATE TABLE t1(a, b UNIQUE);

CREATE TABLE t1(a, b PRIMARY KEY);

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

SQL As Understood By SQLite - CREATE TABLE - SQL Data Constraints

但是,我不认为您可以在独立定义索引时编写冲突子句。所以         CREATE UNIQUE INDEX user_ids_and_telephones ON telephones(user_id, telephone) ON CONFLICT IGNORE无效。

因此,冲突处理将有所不同。

例如,考虑以下内容:-

DROP TABLE IF EXISTS `telephones1`;
CREATE TABLE IF NOT EXISTS `telephones1`(
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
telephone STRING NOT NULL);
DROP INDEX IF EXISTS user_ids_and_telephones;
CREATE UNIQUE INDEX `user_ids_and_telephones` ON `telephones1`(`user_id`, `telephone`)
    -- ON CONFLICT IGNORE commented out as is invalid
;

DROP TABLE IF EXISTS `telephones2`;
CREATE TABLE IF NOT EXISTS `telephones2`(
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
telephone STRING NOT NULL,
UNIQUE(`user_id`, `telephone`) ON CONFLICT IGNORE);

SELECT * FROM sqlite_master WHERE type = 'index' AND name LIKE '%telephones%';


INSERT INTO `telephones2` VALUES
    (null,1,'phone1'),(null,2,'phone2'),(null,3,'phone1'),(null,1,'phone1');
INSERT INTO `telephones1` VALUES
    (null,1,'phone1'),(null,2,'phone2'),(null,3,'phone1'),(null,1,'phone1');
  • 插入电话2不会失败,而只能插入4行中的3行。
  • 插入电话1失败,未插入任何行。

按照:-

DROP TABLE IF EXISTS `telephones1`
> OK
> Time: 0.389s


CREATE TABLE IF NOT EXISTS `telephones1`(
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
telephone STRING NOT NULL)
> OK
> Time: 0.31s


DROP INDEX IF EXISTS user_ids_and_telephones
> OK
> Time: 0s


CREATE UNIQUE INDEX `user_ids_and_telephones` ON `telephones1`(`user_id`, `telephone`)
    -- ON CONFLICT IGNORE
> OK
> Time: 0.366s


DROP TABLE IF EXISTS `telephones2`
> OK
> Time: 0.383s


CREATE TABLE IF NOT EXISTS `telephones2`(
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
telephone STRING NOT NULL,
UNIQUE(`user_id`, `telephone`) ON CONFLICT IGNORE)
> OK
> Time: 0.358s


SELECT * FROM sqlite_master WHERE type = 'index' AND name LIKE '%telephones%'
> OK
> Time: 0s


INSERT INTO `telephones2` VALUES
    (null,1,'phone1'),(null,2,'phone2'),(null,3,'phone1'),(null,1,'phone1')
> Affected rows: 3
> Time: 0.356s


INSERT INTO `telephones1` VALUES
    (null,1,'phone1'),(null,2,'phone2'),(null,3,'phone1'),(null,1,'phone1')
> UNIQUE constraint failed: telephones1.user_id, telephones1.telephone
> Time: 0.004s

从slqite_master查询的输出可以看出,实际上创建了两个索引:-

enter image description here

电话2 上的附件是自动生成的索引(即,它以 sqlite_autoindex 开头)