与Unique筛选索引合并

Rom*_*kar 6 sql sql-server merge sql-server-2008-r2 filtered-index

所以今天我发现了一个奇怪的SQL Server行为.

假设我有一个这样的表,id是主键

??????????????????????
? id ? name ? active ?
??????????????????????
?  1 ? a    ?      0 ?
?  2 ? a    ?      1 ?
??????????????????????
Run Code Online (Sandbox Code Playgroud)

假设我有一个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
Run Code Online (Sandbox Code Playgroud)

工作正常.但如果我尝试合并:

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;
Run Code Online (Sandbox Code Playgroud)

如果因错误而失败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 ?
??????????????????????
Run Code Online (Sandbox Code Playgroud)

并像这样合并:

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;
Run Code Online (Sandbox Code Playgroud)

它再次正常工作.所以看起来看起来像merge会逐行更新并在每行之后检查indexe.我检查了唯一约束,没有过滤器的唯一索引,它都工作正常.它只在我合并合并和过滤索引时失败.

所以问题是 - 这是一个错误,如果是,那么最好的解决方法是什么?

您可以在sql小提琴演示中尝试它.

Rom*_*kar 1

我在 sqlblog.com 上找到了这篇文章 - MERGE Bug with Filtered Indexes ,由Paul White撰写,日期为 2012 年。

\n\n

他给出了几个解决方法:

\n\n
\n
    \n
  • 将过滤索引\xe2\x80\x99s WHERE 子句中引用的所有列添加到索引键(INCLUDE 是不够的);或者
  • \n
  • 使用设置的跟踪标志 8790 执行查询,例如 OPTION (QUERYTRACEON 8790)。
  • \n
\n
\n\n

经过一番研究后,我发现如果我将主键列添加到更新中,它就可以正常工作,因此查询变为:

\n\n
merge Table1 as t\nusing (values (1, 1), (2, 0)) as n(id, active) on n.id = t.id\nwhen matched then\n    update set active = n.active, id = n.id;\n
Run Code Online (Sandbox Code Playgroud)\n\n

我认为也可以从更新的索引添加列,但尚未测试。

\n\n

sql fiddle demo

\n