在MySQL中盲目使用INSERT是否有缺点?

时间:2020-03-06 14:53:11  来源:igfitidea点击:

通常,我想向表中添加一个值,或者如果它的键已经存在,则更新该值。假设在示例中的" user_id"和" pref_key"列上设置了主键或者唯一键,则可以通过多种方式完成此操作:

1.盲插入,如果收到重复的密钥错误,请更新:

// Try to insert as a new value
INSERT INTO my_prefs 
(user_id, pref_key, pref_value)
VALUES (1234, 'show_help', 'true');

// If a duplicate-key error occurs run an update query
UPDATE my_prefs 
SET pref_value = 'true'
WHERE user_id=1234 AND pref_key='show_help';

2.检查是否存在,然后选择或者更新:

// Check for existence
SELECT COUNT(*) 
FROM my_prefs
WHERE user_id=1234 AND pref_key='show_help';

// If count is zero, insert
INSERT INTO my_prefs 
(user_id, pref_key, pref_value) 
VALUES (1234, 'show_help', 'true');

// If count is one, update
UPDATE my_prefs 
SET pref_value = 'true' 
WHERE user_id=1234 AND pref_key='show_help';

第一种方法似乎是更可取的,因为它将仅需要一个查询来插入新插入物,而只需要两个查询来进行更新,而第二种方法将总是需要两个查询。我有什么想念的,尽管盲目插入将是一个坏主意吗?

解决方案

据我所知,第一种方法是首选方法。

就我个人而言,我从来都不喜欢基于异常的编程(期望应用程序的正常运行中会出现异常),对我而言,第二个示例更具可读性/可维护性。

在某些情况下这会有所作为(例如,非常紧密的循环),但我认为应该有充分的理由编写这样的代码,而不是将其作为默认代码。

在DAO模型中,我们可以有一个id字段。

  • 如果设置为null / -1 /任何值,则表示该数据尚未保留。
  • 当我们将其持久化(或者从数据库中检索)时,将其设置为数据库中的id值。
  • persist方法可以检查ID并将其传递到update()或者add()实现。
  • 缺陷:与数据库不同步,等等。我敢肯定还有更多,但是我真的应该做一些工作...

有第三种MySQL方式,它将是该RDBMS中的首选方式

INSERT INTO my_prefs 
(user_id, pref_key, pref_value) 
VALUES (1234, 'show_help', 'true')
ON DUPLICATE KEY 
UPDATE pref_value = 'true'

我们可能可以改用REPLACE,或者如果使用较新的MySQL,则可以选择使用" INSERT ... ON DUPLICATE KEY UPDATE"

有几个人迅速提出来的事实说,当我们遇到问题时,"总是检查MySQL文档",因为它们很不错,而且在许多情况下,直接导致了解决方案。

看看http://dev.mysql.com/doc/refman/5.0/en/insert-select.html中的ON DUPLICATE KEY语法

INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]
[INTO] tbl_name [(col_name,...)]
SELECT ...
[ ON DUPLICATE KEY UPDATE col_name=expr, ... ]

只要使用MySQL,就可以使用ON DUPLICATE关键字。例如:

INSERT INTO my_prefs (user_id, pref_key, pref_value) VALUES (1234, 'show_help', 'true') 
ON DUPLICATE KEY UPDATE (pref_key, pref_value) VALUES ('show_help', 'true');

如果我们想通过插入一个doublette来避免"异常",并且想要使用标准的SQL(并且编程语言/数据库返回已更新的行数),请使用以下" SQL"命令(伪代码):

int i = SQL("UPDATE my_prefs ...");
if(i==0) {
    SQL("INSERT INTO my_prefs ...");
}

这还考虑到在大多数情况下,更新的发生确实比插入发生的频率更高。

这些行是否会有并发INSERT?删除?

只要我们不担心可移植到非MySQL数据库的问题," ON DUPLICATE"听起来就很好(行为正是我们想要的)。

如果从不删除行,则"盲插入"似乎是合理且健壮的。 (如果INSERT情况由于该行存在而失败,则此后UPDATE应该会成功,因为该行仍然存在。但是,如果删除行,则此假设是错误的,那么我们将需要重试逻辑。)在没有" ON DUPLICATE"的其他数据库上,我们如果发现延迟很糟糕,则可能会考虑进行优化:在已经存在的情况下,可以通过将这种逻辑放入存储过程中来避免数据库往返。

如果存在并发INSERT,则"检查是否存在"很难正确。可以在SELECT和UPDATE之间添加行。事务甚至都无法真正帮助我,即使在隔离级别"可序列化",我们也会偶尔看到"由于并发更新而无法序列化访问"错误(或者任何与MySQL等效的错误消息)。我们将需要重试逻辑,所以我想说以上建议使用此方法来避免"基于异常的编程"的人是错误的,而出于相同原因建议首先执行UPDATE的人也是错误的。