SQLite 插入 - 重复键更新 (UPSERT)

Posted

技术标签:

【中文标题】SQLite 插入 - 重复键更新 (UPSERT)【英文标题】:SQLite INSERT - ON DUPLICATE KEY UPDATE (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 中不存在此功能,我想知道是否有任何方法可以实现相同的效果而无需执行两个查询。另外,如果这不可能,你更喜欢什么:

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

【问题讨论】:

【参考方案1】:

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

【讨论】:

谢谢! @Sam (***.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://***.com/a/4330694/89771。

【讨论】:

仅作记录,REPLACE 不是一个选项。 关于“另一个伟大的解决方案”链接,我也会考虑对同一问题的不同答案:***.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?

以上是关于SQLite 插入 - 重复键更新 (UPSERT)的主要内容,如果未能解决你的问题,请参考以下文章

SQLite UPSERT / 更新或插入

如何在 PostgreSQL 中进行 UPSERT(合并、插入……重复更新)?

MS Access UPSERT(更新/插入)SQL [重复]

mysql实现upsert

关于在MySQL中实现upsert操作

Mongoose upsert 重复键错误