【问题标题】:SQLite INSERT - ON DUPLICATE KEY UPDATE (UPSERT)SQLite 插入 - 重复键更新 (UPSERT)
【发布时间】:2011-02-12 15:21:57
【问题描述】:

MySQL 有这样的东西:

INSERT INTO visits (ip, hits)
VALUES ('127.0.0.1', 1)
ON DUPLICATE KEY UPDATE hits = hits + 1;

据我所知 SQLite 中不存在此功能,我想知道是否有任何方法可以实现相同的效果而无需执行两个查询。另外,如果这不可能,你更喜欢什么:

  1. SELECT +(插入或更新)
  2. 更新(+ INSERT 如果更新失败

【问题讨论】:

    标签: sql mysql database sqlite upsert


    【解决方案1】:

    我更喜欢UPDATE (+ INSERT if UPDATE fails)。更少的代码 = 更少的错误。

    【讨论】:

    • 谢谢! @Sam (stackoverflow.com/questions/418898/…) 似乎同意你的看法。我也更喜欢这种方法。
    • @Smith 我的意思是使用普通的 UPDATE 和 INSERT 语句并检查返回值。
    • 这没有原子性,如果在中间插入其他进程,INSERT 可能会失败。
    【解决方案2】:
    INSERT OR IGNORE INTO visits VALUES ($ip, 0);
    UPDATE visits SET hits = hits + 1 WHERE ip LIKE $ip;
    

    这要求“ip”列具有唯一(或主键)约束。


    编辑:另一个很好的解决方案:https://stackoverflow.com/a/4330694/89771

    【讨论】:

    • 仅作记录,REPLACE 不是一个选项。
    • 关于“另一个伟大的解决方案”链接,我也会考虑对同一问题的不同答案:stackoverflow.com/a/418988/3650835
    【解决方案3】:

    您应该为此使用 memcached,因为它是存储单个值(访问次数)的单个键(IP 地址)。您可以使用原子增量函数来确保没有“竞争”条件。

    它比 MySQL 更快,并且节省了负载,因此 MySQL 可以专注于其他事情。

    【讨论】:

    • 如果数据不是那么重要,是的。但是,如果在有许多 IP 访问服务的繁忙站点上使用此功能,则 memcached 实例可能会变满并导致某些内容被丢弃。备份 memcached 内容也很有趣(如果需要)。
    • @ElliotFoster Memcached 可以处理与您投入的 RAM 一样多的数据(如果您还想要持久性,请使用 redis 或 membase)。如果您每天的访问量超过 100 万,那么您可能可以为您的 memcache 实例提供超过 30MB 的内存(我认为这是默认值)。然而,就你给它的内存量而言,它肯定可以处理比 SQLite 和 MySQL 高得多的负载——没有任何可比性。
    • 请不要将我的评论误认为是对 memcache 的投票,因为我认为这是一个很棒的工具。和 redis 一样(我不能代表 membase,因为我没有使用过它。)但是,memcache/redis 并不是最可靠的存储。是的,redis 具有持久性,但是数据会以一定的时间间隔(我最后一次查看)持久化到磁盘,而 memcache 则根本没有。就像我说的,如果数据不重要(或者可以轻松复制),那么 memcache 和公司就很棒。原始帖子还询问了 sqlite,它与 MySQL 非常不同,可能意味着它们在其他方面受到限制。
    • 您可以配置 Redis 以尽可能快地保存数据(X 秒或 Y 次更改)。
    【解决方案4】:

    当前答案仅适用于 sqlite OR mysql(取决于您是否使用 OR)。所以,如果你想要跨 dbms 的兼容性,下面会做...

    REPLACE INTO `visits` (ip, value) VALUES ($ip, 0);
    

    【讨论】:

    • 接受的答案适用于 SQLite(这是我的目标)。 REPLACE 也可以在 SQLite 上工作,但在 MySQL 上,它总是会将计数器重置为 0 - 虽然查询是可移植的,但最终结果会有很大差异。
    • 你说得对,我认为 OP 正在寻找可移植的东西。我意识到 REPLACE INTO 不适用于所有情况,尤其是需要 PK 保存的情况,但适用于许多情况。
    • 彻底失败而不是丢弃数据是一项功能,而不是错误。
    • 替换总是先删除行,所以很乱。
    【解决方案5】:

    Since 3.24.0 SQLite also supports upsert,所以现在你可以简单地写如下

    INSERT INTO visits (ip, hits)
    VALUES ('127.0.0.1', 1)
    ON CONFLICT(ip) DO UPDATE SET hits = hits + 1;
    

    【讨论】:

    • 我想知道你是否可以在一个事务中执行多个这样的upsert,即使用 Python executemany() 函数?
    • {"SQL 逻辑错误或缺少数据库\r\nnear \"ON\": 语法错误" 执行上述命令时出现异常
    • @jithu 您使用的是哪个版本的 SQLite?
    猜你喜欢
    • 2013-02-23
    • 1970-01-01
    • 1970-01-01
    • 2013-08-02
    • 2021-06-02
    • 2012-04-21
    • 2021-03-28
    • 2013-06-20
    相关资源
    最近更新 更多