MySQL和SQLite的唯一约束和插入或更新

时间:2018-06-20 14:41:50

标签: java mysql sqlite mariadb

我正在寻找一种将一组列定义为唯一的方法,然后在表中插入新条目,或者如果列不是唯一的则更新行。我已经进行了一些研究,找到了解决方法,但是找不到与MySQL和SQLite兼容的任何东西。

说我有下表:

CREATE TABLE `users` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `uuid` VARCHAR ( 64 ) NOT NULL,
  `name` VARCHAR ( 32 ) NOT NULL,
  `date` BIGINT NULL,
  PRIMARY KEY ( id )
);

我希望uuiddate唯一,以便一个uuid或一个date可以有多个条目,但{{1 }}和uuid。我最初想要做的是将主键设置为这些键:

date

但是,由于某些原因,我在执行此操作时将无法为PRIMARY KEY ( uuid, date ) 使用空值。
我还发现了有关约束的一些信息,但不确定是否可行:

date

现在,我想在此表中插入新行,但是如果已经存在具有相同CONSTRAINT user UNIQUE ( `uuid`, `date` ) uuid的行,请更新现有行。我发现了几种方法,但是它们要么没有按照我的意愿做,要么与MySQL和SQLite都不兼容:

  • date不适用于SQLite
  • INSERT INTO ... ON DUPLICATE KEY将删除我未指定的所有内容,而不是进行更新

我已经进行了一段时间的研究,但是找不到适合我的解决方案。任何帮助表示赞赏。

3 个答案:

答案 0 :(得分:1)

SQLite解决方案(相同的原理在mysql中也应适用)

您可以简单地添加一个UNIQUE索引(至少对于SQLite而言),因此您可以:-

DROP TABLE IF EXISTS `users`;
CREATE TABLE IF NOT EXISTS `users` (
  `id` INTEGER, //<<<<<<<<<< See notes below
  `uuid` VARCHAR ( 64 ) NOT NULL,
  `name` VARCHAR ( 32 ) NOT NULL,
  `date` BIGINT NULL,
  PRIMARY KEY ( `id` )
);
CREATE UNIQUE INDEX IF NOT EXISTS uuid_date ON `users` (`uuid`,`date`); //<<<<<<<<<<
  • 注意AUTO_INCREMENT导致SQLite失败,因为它不是关键字,SQlite中正确的关键字是AUTOINCREMENT。但是,它被省略了,因为它可能不是必需的,因为如果在插入时未为列提供任何值,则INTEGER PRIMARY KEY(或通过指定PRIMARY KEY (id)进行隐式)将导致自动生成uniqiue id。

  • 对于自动生成的ID,SQLite需要INTEGER(而不是INT)。隐含NOT NULL和UNIQUE,因此无需指定它们。

这里有两套示例插入,每套都复制了uuid / date组合,从而进行更新,而不是插入,并且也插入了相同的uuid但插入了不同的日期,反之亦然:-

INSERT OR REPLACE INTO `users` VALUES(null,'Fred01234567','Fred Bloggs the 1st','20180101');
INSERT OR REPLACE INTO `users` VALUES(null,'Fred01234567','Fred Bloggs the 2nd','20180101'); -- <<<< DUPLICATE 
INSERT OR REPLACE INTO `users` VALUES(null,'Fred99999999','Fred Bloggs the 2nd','20180101'); -- <<<< different uuid same date
INSERT OR REPLACE INTO `users` VALUES(null,'Fred01234567','Fred Bloggs the 2nd','99999999'); -- <<<< same uuid different date

INSERT OR REPLACE INTO `users` (`uuid`,'name','date') VALUES('Fred76543210','Fred NotBloggs the 1st','20180202');
INSERT OR REPLACE INTO `users` (`uuid`,'name','date') VALUES('Fred76543210','Fred NotBloggs the 1st','20180202');
INSERT OR REPLACE INTO `users` (`uuid`,'name','date') VALUES('Fred99999999','Fred NotBloggs the 1st','20180202');
INSERT OR REPLACE INTO `users` (`uuid`,'name','date') VALUES('Fred76543210','Fred NotBloggs the 1st','99999999');

SELECT * FROM `users`;

结果是:-

enter image description here

答案 1 :(得分:1)

我已经搜索了几个小时,并使用MySQL和SQLite进行了一些测试,我认为我找到了一个可行的解决方案。
为了使uuiddate的组合唯一,我在表中添加了唯一约束。要插入新行或“更新”现有行,我正在使用REPLACE INTO ... SELECT

要创建表:

CREATE TABLE IF NOT EXISTS `users` (
  `id` INT NOT NULL AUTO_INCREMENT, // use INTEGER NOT NULL for SQLite
  `uuid` VARCHAR ( 64 ) NOT NULL,
  `name` VARCHAR ( 32 ) NOT NULL,
  `date` BIGINT NULL,
  CONSTRAINT `uuid_date` UNIQUE ( `uuid`, `date` ),
  PRIMARY KEY ( `id` )
);

CONSTRAINT将确保uuiddate的组合始终是唯一的。
为了插入数据,我使用REPLACE INTO ... SELECT,在SELECT查询中输入所有(新)值,并为所有我未指定值的列输入列名,以确保其保持不变它们的值保持不变,而不是在替换现有行时将其删除。

REPLACE INTO `users`
SELECT `id`, `uuid`, ?, `date`
FROM `users` WHERE `uuid` = ? AND `date` = ?;

当然,因为在这种情况下,使用普通的REPLACE INTO时不会丢失任何列,因此我也可以使用:

REPLACE INTO `users` ( `uuid`, `name`, `date` ) VALUES ( ?, ?, ? );

但是,当我的表中的列更多时,REPLACE INTO ... SELECT查询可能会有用,而实际上,如果不选择它们,则可能会丢失列。

答案 2 :(得分:0)

对不起,所有评论。这就是我实现我认为您要追求的目标的方式。您将在用户表上丢失ID作为主键。您还必须将插入变量放在表中。但是,您将获得最终结果。抱歉,我没有更好的解决方案。

DROP TABLE users;
DROP TABLE users2;

CREATE TABLE `users` (
`uuid` VARCHAR ( 64 ) NOT NULL,
`name` VARCHAR ( 32 ) NOT NULL,
`date` BIGINT NULL

);

ALTER TABLE `users` ADD PRIMARY KEY(`uuid`,`date`);

INSERT INTO users (`name`,`uuid`,`date`) SELECT '','123','2018-04-01';

CREATE TABLE `users2` (

`uuid` VARCHAR ( 64 ) NOT NULL,
`name` VARCHAR ( 32 ) NOT NULL,
`date` BIGINT NULL

);

INSERT INTO users2 (`name`,`uuid`,`date`) SELECT 'brad','123','2018-04-01';

REPLACE INTO users SELECT `uuid`,`name`,`date` FROM users2 GROUP BY `uuid`,`date`;

SELECT * FROM users;`