需要便携式SQL upsert(插入+更新)解决方案

时间:2010-12-21 01:27:34

标签: sql mysql sql-server database oracle

我需要对数据库执行一个非常简单的操作 - 按键将一些值放入表中,如果行存在 - 更新它,如果不存在 - 创建一个新值。问题是我需要以可移植的方式进行 - 即相同的查询必须适用于MySQL,SQL Server,Oracle,最好也适用于DB2,Postgres等。所以我可以使用REPLACE或{{1在MySQL中,我不确定这些语言会支持这种语法。我真的想避免使用数据库类型,因为这是不可维护的。我也不想在更新之前实际查询该值,因为我怀疑它会显着减慢该过程(我需要多次执行)。

到目前为止,我想出的最好的就是:

INSERT ... ON DUPLICATE KEY UPDATE

其中一个总会成功,另一个会失败,但我并不关心其中一个查询失败。虽然看起来不那么优雅。有什么建议如何让它变得更好?

3 个答案:

答案 0 :(得分:5)

使用一些现代数据库的唯一完全与数据库无关的解决方案是在两个操作中调用Update然后调用Insert。某些数据库不允许在单个操作中发送多个语句,而某些数据库可能不会返回受Update影响的行数,因此我不会依赖它。

Update MyTable
Set Data = 'Data'
Where KeyCol = 'key';

(单独电话)

Insert Into MyTable(KeyCol, Data)
Select 'key', 'Data'
From ( Select 1 As Value ) As Z
Where Not Exists    (
                    Select 1
                    From MyTable As T1
                    Where T1.KeyCol = 'key'
                    );

答案 1 :(得分:2)

首先执行UPDATE。将通知您的客户端代码更新的行数。如果更新的行计数为零,则执行INSERT

更新的行计数返回到客户端代码的确切方式取决于您使用的数据库访问库。例如,在Java中,它是PreparedStatement.executeUpdate()的返回值,在Python DB-API中使用cursor.rowcount访问它。

您需要确保整个过程在可序列化的事务中发生,以避免与并发编写者发生冲突。

答案 2 :(得分:1)

MERGE和WHEN NOT MATCHED是ANSI SQL 2003的一部分。当然,它不是统一支持的。

你聪明的解决方案很危险,因为它不清楚客户应该做什么。忽略所有错误?如果是这样,如果它们都失败了会发生什么。

相反,不要试图使这个可移植。使用存储过程和/或DAL来处理后端之间的差异,因为在任何情况下这无疑都不是您唯一的问题。