2013-10-22 14 views
6

Odkryłem dziwne zachowanie SQL Server dzisiaj.Scal z unikalnym filtrującym indeksem

Załóżmy, że mam tabeli tak, id jest klucz podstawowy

╔════╦══════╦════════╗ 
║ id ║ name ║ active ║ 
╠════╬══════╬════════╣ 
║ 1 ║ a ║  0 ║ 
║ 2 ║ a ║  1 ║ 
╚════╩══════╩════════╝ 

I załóżmy mam filtered unique index on name where active = 1. Teraz chcę tylko włączyć przełącznik aktywny dla wierszy, ustawić pierwszy wiersz jako nieaktywny i ustawić drugi wiersz aktywny. Kiedy staram się robić to jak zaktualizować

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 

to działa prawidłowo. Ale gdy próbuję zrobić Merge:

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; 

jeśli udało z błędem Cannot insert duplicate key row in object 'dbo.Table1' with unique index 'ix_Table1'. The duplicate key value is (a).

Nawet obcy, jeśli ma stół tak (pierwszego rzędu są aktywne = 1, a w drugim rzędzie ma czynną = 0):

╔════╦══════╦════════╗ 
║ id ║ name ║ active ║ 
╠════╬══════╬════════╣ 
║ 1 ║ a ║  1 ║ 
║ 2 ║ a ║  0 ║ 
╚════╩══════╩════════╝ 

i połączenie go w następujący sposób:

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; 

iT znowu działa dobrze. Tak naprawdę wygląda na to, że scalanie wykonuje aktualizacje wiersz po wierszu i sprawdzanie indexe po każdym wierszu. Sprawdziłem unikalne ograniczenia, unikalne indeksy bez filtra, wszystko działa dobrze. Występuje on tylko wtedy, gdy łączę scalony i przefiltrowany indeks.

Pytanie brzmi - czy jest to błąd, a jeśli tak, jaki jest najlepszy sposób obejścia tego problemu?

Możesz wypróbować na sql fiddle demo.

Odpowiedz

1

Znalazłem ten artykuł na sqlblog.com - MERGE Bug with Filtered Indexes, on napisany przez Paul White, datowany 2012.

Dał kilka obejścia:

  • Dodanie wszystkie kolumny odwołuje się indeks WHERE dla indeksu filtrowanego do klucza indeksu (funkcja INCLUDE nie jest wystarczająca); lub
  • Wykonywanie zapytania z flagą śledzenia 8790 zestaw np. OPCJA (QUERYTRACEON 8790).

Po trochę badań odkryłem, że jeśli dodać podstawową kolumnę klucza do aktualizacji, to działa ok, więc zapytania staje:

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; 

myślę, że jest to również możliwe, aby dodaj kolumnę z zaktualizowanego indeksu, ale jeszcze jej nie testowałem.

sql fiddle demo