代码之家  ›  专栏  ›  技术社区  ›  bgusach

SQLite升级/更新或插入

  •  129
  • bgusach  · 技术社区  · 12 年前

    我需要对SQLite数据库执行UPSERT/INSERT或UPDATE操作。

    有一个命令INSERT OR REPLACE,它在许多情况下都很有用。但是,如果由于外键的原因,您想保留带有自动递增的id,那么它就不起作用了,因为它删除了行,创建了一个新的行,因此这个新行有了新的id。

    这将是表格:

    players-(id上的主键,user_name唯一)

    |  id   | user_name |  age   |
    ------------------------------
    |  1982 |   johnny  |  23    |
    |  1983 |   steven  |  29    |
    |  1984 |   pepee   |  40    |
    
    8 回复  |  直到 9 年前
        1
  •  0
  •   CashCow    3 年前

    问&A风格

    好吧,在研究并解决了这个问题数小时后,我发现有两种方法可以实现这一点,这取决于表的结构以及是否激活了外键限制以保持完整性。我想以一种干净的形式分享这篇文章,为可能处于我这种情况的人节省一些时间。


    选项1:您可以删除该行

    换句话说,您没有外键,或者如果您有外键,您的SQLite引擎会被配置为不存在完整性异常。要走的路是 插入或更换 。如果您试图插入/更新ID已经存在的玩家,SQLite引擎将删除该行并插入您提供的数据。现在问题来了:如何保持旧ID的关联?

    假设我们想 追加销售 数据user_name=“偶数”,年龄=32岁。

    查看此代码:

    INSERT INTO players (id, name, age)
    
    VALUES (
        coalesce((select id from players where user_name='steven'),
                 (select max(id) from drawings) + 1),
        32)
    

    诀窍在于联合。它返回用户“steven”的id(如果有的话),否则,它将返回一个新的id。


    选项2:您无法承担删除行的费用

    在对以前的解决方案进行了修改后,我意识到在我的情况下,这可能最终会破坏数据,因为这个ID可以作为其他表的外键。此外,我用子句创建了表格 删除级联时 ,这意味着它将以静默方式删除数据。危险的

    所以,我首先想到了IF子句,但SQLite只有 案例 。还有这个 案例 不能被用来执行(或者至少我没有管理它) 更新 查询是否存在(从玩家中选择id,其中user_name=“偶数”),以及 插入 如果没有的话。不行。

    然后,最后我使用了蛮力,并取得了成功。逻辑是,对于每个 追加销售 要执行的,首先执行 插入或忽略 以确保与我们的用户有一行,然后执行 更新 使用与您尝试插入的数据完全相同的数据进行查询。

    数据与以前相同:用户名称=“偶数”,年龄=32岁。

    -- make sure it exists
    INSERT OR IGNORE INTO players (user_name, age) VALUES ('steven', 32); 
    
    -- make sure it has the right data
    UPDATE players SET user_name='steven', age=32 WHERE user_name='steven'; 
    

    仅此而已!

    编辑

    正如Andy所评论的那样,尝试先插入然后更新可能会导致比预期更频繁地触发。在我看来,这不是数据安全问题,但确实,引发不必要的事件毫无意义。因此,一个改进的解决方案是:

    -- Try to update any existing row
    UPDATE players SET age=32 WHERE user_name='steven';
    
    -- Make sure it exists
    INSERT OR IGNORE INTO players (user_name, age) VALUES ('steven', 32);