所以我今天发现了一个奇怪的SQL Server行为。
假设我有一个这样的表,id是主键
╔════╦══════╦════════╗
║ id ║ name ║ active ║
╠════╬══════╬════════╣
║ 1 ║ a ║ 0 ║
║ 2 ║ a ║ 1 ║
╚════╩══════╩════════╝
假设我有filtered unique index on name where active = 1
。
现在,我只想切换活动行,将第一行设置为非活动状态并将第二行设置为活动状态。当我尝试更新它时
update Table1 set
active = n.active
from Table1 as t
inner join (values (1, 1), (2, 0)) as n(id, active) on n.id = t.id
正常运行。但如果我尝试合并:
merge Table1 as t
using (values (1, 1), (2, 0)) as n(id, active) on n.id = t.id
when matched then
update set active = n.active;
如果失败,错误为Cannot insert duplicate key row in object 'dbo.Table1' with unique index 'ix_Table1'. The duplicate key value is (a)
。
更奇怪的是,如果我有这样的表(第一行有active = 1而第二行有active = 0):
╔════╦══════╦════════╗
║ id ║ name ║ active ║
╠════╬══════╬════════╣
║ 1 ║ a ║ 1 ║
║ 2 ║ a ║ 0 ║
╚════╩══════╩════════╝
并像这样合并:
merge Table1 as t
using (values (1, 0), (2, 1)) as n(id, active) on n.id = t.id
when matched then
update set active = n.active;
再次正常工作。因此,看起来合并确实会逐行更新 并在每行后检查indexe。我检查了唯一约束,没有过滤器的唯一索引,它都工作正常。它只在合并和过滤索引时才会失效。
所以问题是 - 它是一个错误,如果是,那么最好的解决方法是什么?
您可以在 sql fiddle demo 上试用。
答案 0 :(得分:1)
我在sqlblog.com上发现了这篇文章 - MERGE Bug with Filtered Indexes,由Paul White撰写,日期为2012年。
他提供了几个解决方法:
- 将过滤索引的WHERE子句中引用的所有列添加到索引键(INCLUDE是不够的);或
- 执行带有跟踪标志8790的查询,例如选项(QUERYTRACEON 8790)。
经过一些研究后我发现如果我将主键列添加到更新中,它就可以了,所以查询变为:
merge Table1 as t
using (values (1, 1), (2, 0)) as n(id, active) on n.id = t.id
when matched then
update set active = n.active, id = n.id;
我认为也可以从更新索引中添加列,但尚未对其进行测试。
<强> sql fiddle demo 强>