我需要为 MS-Access 2000 编写一个 SQL 查询,以便在存在时更新行,如果不存在则插入。(我相信这被称为“upsert”)
IE
如果行存在...
UPDATE Table1 SET (...) WHERE Column1='SomeValue'
如果它不存在...
INSERT INTO Table1 VALUES (...)
这可以在一个查询中完成吗?
您可以通过使用UPDATE
带有LEFT JOIN
.
update b
left join a on b.id=a.id
set a.f1=b.f1
, a.f2=b.f2
, a.f3=b.f3
假设Column1上的唯一索引,您可以使用表达式来确定您是否有Column1 = 'SomeValue' 的DCount
零行或一行。然后或基于该计数。INSERT
UPDATE
If DCount("*", "Table1", "Column1 = 'SomeValue'") = 0 Then
Debug.Print "do INSERT"
Else
Debug.Print "do UPDATE"
End If
我更喜欢这种方法,首先尝试一个INSERT
,捕获 3022 密钥违规错误,然后UPDATE
响应该错误。但是,我不能从我的方法中获得巨大的好处。如果您的表包含自动编号字段,避免失败INSERT
将阻止您不必要地花费下一个自动编号值。我还可以避免INSERT
在不需要时构建字符串。Access Cookbook告诉我,字符串连接在 VBA 中是一项成本适中的操作,因此我寻找机会避免构建字符串,除非确实需要它们。这种方法还可以避免为不需要的INSERT
.
但是,这些原因对您来说可能都不是很有吸引力。老实说,我认为我在这种情况下的偏好可能是关于我“感觉正确”的东西。我同意@David-W-Fenton对上一个 Stack Overflow 问题的评论:“最好编写 SQL,这样您就不要尝试附加已经存在的值 - 即,防止错误发生在第一个而不是依靠数据库引擎来拯救你自己。”
如果表具有唯一键,则可以进行“更新插入”。
Smart Access的这个旧技巧是我的最爱之一:
一次查询更新和追加记录
艾伦·比格斯
您是否知道可以在 Access 中使用更新查询同时更新和添加记录?如果您有两个版本的表 tblOld 和 tblNew,并且您希望将 tblNew 中的更改集成到 tblOld 中,这将很有用。
跟着这些步骤:
创建更新查询并添加两个表。通过将 tblNew 的键字段拖到 tblOld 的匹配字段上来连接两个表。
双击关系并选择包含来自 tblNew 的所有记录以及仅来自 tblOld 的那些记录的连接选项。
从 tblOld 中选择所有字段并将它们拖到 QBE 网格上。
对于每个字段,在更新到单元格中键入 tblNew.FieldName,其中 FieldName 与 tblOld 的字段名称匹配。
从 View 菜单中选择 Query Properties 并将 Unique Records 更改为 False。(这会关闭 SQL 视图中的 DISTINCTROW 选项。如果您保留此选项,您将在结果中仅获得一条空白记录,但您希望将每条新记录添加到 tblOld 中的一条空白记录。)
运行查询,您将看到对 tblNew 的更改现在位于 tblOld 中。
这只会将已添加到 tblNew 的记录添加到 tblOld。tblOld 中不存在于 tblNew 中的记录仍将保留在 tblOld 中。
我通常先运行插入语句,然后检查是否出现错误 3022,这表明该行已经存在。所以是这样的:
On Error Resume Next
CurrentDb.Execute "INSERT INTO Table1 (Fields) VALUES (Data)", dbFailOnError
If Err.Number = 3022 Then
Err.Clear
CurrentDb.Execute "UPDATE Table1 SET (Fields = Values) WHERE Column1 = 'SomeValue'", dbFailOnError
ElseIf Err.Number <> 0 Then
'Handle the error here
Err.Clear
End If
Edit1:
我想提一下,我在这里发布的是一个非常常见的解决方案,但您应该知道,计划错误并将它们用作程序正常流程的一部分通常被认为是一个坏主意,特别是如果有达到相同结果的其他方法。感谢 RolandTumble 指出这一点。
您不需要捕获错误。相反,只需运行 INSERT 语句,然后检查
CurrentDb.RecordsAffected
它将是 1 或 0,具体取决于。
注意:对 CurrentDB 执行不是一个好习惯。最好将数据库捕获到局部变量:
Dim db As DAO.Database
Set db = CurrentDb
db.Execute(INSERT...)
If db.RecordsAffected = 0 Then
db.Execute(UPDATE...)
End If
正如其他人所提到的,您可以UPSERT
使用UPDATE LEFT JOIN
新表作为左侧。这将添加所有丢失的记录并更新匹配的记录,而保留已删除的记录不变。
如果我们遵循创建并运行更新查询文章,我们最终会得到如下所示的 SQL:
UPDATE Table1
INNER JOIN NewTable1 ON Table1.ID = NewTable1.ID
SET Table1.FirstName = [NewTable1].[FirstName]
但是内连接只会更新匹配的记录,不会添加新记录。因此,让我们将其更改INNER
为LEFT
:
UPDATE Table1
LEFT JOIN NewTable1 ON Table1.ID = NewTable1.ID
SET Table1.FirstName = [NewTable1].[FirstName]
现在保存数据库的副本。在主数据库上运行此副本之前,先在副本上运行一个测试。