【问题标题】:SQLite 'INSERT OR REPLACE INTO' incorrect behaviourSQLite 'INSERT OR REPLACE INTO' 不正确的行为
【发布时间】:2016-08-09 09:34:24
【问题描述】:

我正在使用 SQLite 来存储 iduuidvaluecaught 值。

我用这个命令创建表:

CREATE TABLE IF NOT EXISTS statz_fish_caught 
('id' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
 'uuid' TEXT NOT NULL,
 'value' INTEGER NOT NULL,
 'caught' TEXT NOT NULL);

我使用这个命令来插入(或替换)行:

INSERT OR REPLACE INTO statz_fish_caught (uuid,value,caught) 
VALUES ('uuid comes here', a number here, 'a certain string').

假设您在表中有这些数据:

http://pasteboard.co/gofap3y.png

这个数据意味着 UUID1 总共捕获了 9 (1+5+3) 条鱼。 1 型鱼的 1 条,2 型鱼的 5 条和 3 型鱼的 3 条。UUID2 只钓到了 10 条鱼 1 型的鱼。现在假设 UUID1 钓到了一种新的鱼类型:鱼 4 型。数据库应该创建一个新行id 为 5 的自动递增值,uuid 为 'uuid1',value=1 和 caught=fish type 4。

现在我们还假设 UUID 捕获了另一条鱼类型为 3 的鱼。鱼类型为 3 的 uuid1 行(它的 id 为 3)应该更新为表示值 4,如下所示:

http://pasteboard.co/goBZatx.png

这可能吗,如果可以,怎么做?

【问题讨论】:

    标签: java mysql sqlite primary-key


    【解决方案1】:

    您正在使用INTEGER AUTOINCREMENT PRIMARY KEY,并且您的表中没有任何其他UNIQUE 字段:这意味着现有行与您尝试添加的行之间不可能发生冲突,因此不会有REPLACE

    您应该像这样更改表的定义:

    CREATE TABLE IF NOT EXISTS statz_fish_caught 
    ('id' INTEGER PRIMARY KEY NOT NULL,
     'uuid' TEXT NOT NULL,
     'value' INTEGER NOT NULL,
     'caught' TEXT NOT NULL);
    

    请注意,通过更改此设置,您必须在添加行时手动输入 id 值,因为它不再由 DBMS 管理。

    编辑Here你可以找到一个工作示例;注意主键的值没有改变。

    编辑#2:正如ishmaelMakitla 所建议的,将UNIQUE 放在UUID 字段中应该可以解决问题;我已经相应地更新了我的答案。

    CREATE TABLE IF NOT EXISTS statz_fish_caught 
    ('id' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
     'uuid' TEXT NOT NULL UNIQUE,
     'value' INTEGER NOT NULL,
     'caught' TEXT NOT NULL);
    

    警告:如 SQLite 文档中所述,

    AUTOINCREMENT 关键字会带来额外的 CPU、内存、磁盘空间和磁盘 I/O 开销,如果不是严格需要,应避免使用。通常不需要它。

    另外,正如您在 this 示例中看到的,更新行的 id 已更改,这是我强烈建议避免的事情。

    编辑#3 我花了一些时间,但 this 示例应该可以满足您的需求。

    您必须使用的查询是这样的(6 是您要添加的捕获的鱼):

    INSERT OR REPLACE INTO statz_fish_caught (uuid,value,caught) 
    VALUES (
         -- (select `id` from statz_fish_caught where uuid = 'uu1'),
         'uu1', 
         (select `value` from statz_fish_caught where uuid = 'uu1') + 6, 
         (select `caught` from  statz_fish_caught where uuid = 'uu1')
    );
    

    请注意,如果您想为更新行保留id 字段的当前值,您必须取消对值的第一行的注释,并在语句的列列表中添加id 字段。

    【讨论】:

    • 这确实是我需要的。有没有办法让 DBMS 自动执行 id 列?如果没有,我将如何每次手动更新 id?我首先需要知道 id 是什么,对吧?
    • 要摆脱自动增量,您应该与使用此数据库的人核实是否有其他字段可用作主键。如果由于某种原因您坚持使用自动增量(理论上这不是最好的方法),您可以将另一个字段设置为 UNIQUE,就像其他答案中所建议的那样。这样,Replace 在该字段发生冲突时生效。
    • 看来我太含糊了。我已经用不同的图片更新了我的原始帖子,解释了每列的含义。有没有办法让它与 SQLite 一起工作? (顺便说一句,你的第二个解决方案不是我想要的,对不起!)
    • 一个 UUID 能否在同一个语句中捕获多个特定类型的鱼?我的意思是,你可以添加不止一条鱼吗?
    • 是的,一个 UUID 可以一次捕获任意数量的特定类型的鱼。
    【解决方案2】:

    除了 Cynical 所说的之外,您应该将 UUID 设置为 UNIQUE - 这样,具有匹配 UUID 的行将被替换。所以表应该修改如下:

    CREATE TABLE `statz_fish_caught` (
    `id`    INTEGER NOT NULL,
    `uuid`  TEXT NOT NULL UNIQUE,
    `value` INTEGER NOT NULL,
    `caught`    TEXT NOT NULL,
    PRIMARY KEY(id)
    );
    

    我已通过以下方式对此进行了测试:

    INSERT OR REPLACE INTO statz_fish_caught 
    (uuid,value,caught) VALUES (hex(randomblob(16)), 1, 'caught-1 ');
    

    然后我执行了以下查询字符串(注意,我在前面的查询中使用了 hex(randomblob(16) 生成的 UUID)。所以通过运行:

    INSERT OR REPLACE INTO statz_fish_caught (uuid,value,caught) VALUES ('uuid-of-row-to-update', 2, 'caught-1-updated'); 
    

    【讨论】:

    • 我想我不够清楚。 caught 列可以有多个值,但如果相同则应分组。它应该像this 一样工作,但带有 id 列的自动处理。有可能吗?
    • 在我的回答中,我假设 UUID 将与替换相同。现在,随着您的澄清,“捕获”列必须具有 UNIQUE 约束 - 否则无法知道查询应该替换哪一行。结果,插入了一个新行。当您在小提琴中指定相同的 id 值(1)时它起作用的原因是因为 ID 通过作为主键自动具有唯一约束。因此,您应该修改您的表,使“捕获”列唯一:caught TEXT NOT NULL UNIQUE,
    • 这没有成功。请参阅我的原始帖子,我对其进行了编辑并阐明了我特别希望我的数据库做什么。
    • @Staartvin,因此您似乎需要 匹配 UUIDcaught(捕获的鱼的类型)。因此,基本上,您在两列(uuid 和 catch)上创建了一个唯一约束。这是我认为可以按您期望的那样工作的唯一方法。为此,您在创建表时将 UNIQUE 声明放在查询的列定义部分。请参阅在多列上设置唯一约束的投票答案:stackoverflow.com/questions/2701877/…。我希望这会有所帮助。
    猜你喜欢
    • 1970-01-01
    • 2015-09-21
    • 2010-09-29
    • 1970-01-01
    • 2018-10-06
    • 2013-11-01
    相关资源
    最近更新 更多