Objawy
W przypadku używania funkcji SCOPE_IDENTITY() lub @@IDENTITY w celu pobrania wartości wstawionych do kolumny tożsamości można zauważyć, że te funkcje czasami zwracają nieprawidłowe wartości. Ten problem występuje tylko wtedy, gdy zapytania korzystają z planów wykonywania równoległego.
Przyczyna
Firma Microsoft potwierdziła, że jest to usterka występująca w produktach firmy Microsoft wymienionych na początku tego artykułu.
Rozwiązanie
Informacje o aktualizacji zbiorczej
SQL Server 2008 R2 z dodatkiem Service Pack 1
Poprawkę rozwiązującą ten problem opublikowano po raz pierwszy w aktualizacji zbiorczej 5 dla programu SQL Server 2008 R2 z dodatkiem Service Pack 1. Aby uzyskać więcej informacji dotyczących sposobu uzyskiwania tego pakietu aktualizacji zbiorczej, kliknij następujący numer artykułu w celu wyświetlenia tego artykułu z bazy wiedzy Baza wiedzy Microsoft Knowledge Base:
2659694 Pakiet aktualizacji zbiorczej 5 dla dodatku Service Pack 1 dla systemu SQL Server 2008 R2
Wskazówka Ponieważ te kompilacje są zbiorcze, każda nowa wersja poprawki zawiera wszystkie poprawki i poprawki zabezpieczeń zawarte w poprzedniej wersji poprawki programu SQL Server 2008 R2. Zalecamy rozważenie zastosowania najnowszej wersji poprawki zawierającej tę poprawkę. Aby uzyskać więcej informacji, kliknij następujący numer artykułu w celu wyświetlenia tego artykułu z bazy wiedzy Baza wiedzy Microsoft Knowledge Base:
2567616 Kompilacje SQL Server 2008 R2 wydane po wydaniu dodatku Service Pack 1 SQL Server 2008 R2
Obejście
Firma Microsoft zaleca, aby nie używać żadnej z tych funkcji w zapytaniach, gdy w grę wchodzą plany równoległe, ponieważ nie zawsze są one niezawodne. Zamiast tego użyj klauzuli OUTPUT instrukcji INSERT, aby pobrać wartość tożsamości, jak pokazano w poniższym przykładzie.
Przykład użycia klauzuli OUTPUT:
DECLARE @MyNewIdentityValues table(myidvalues int)
declare @A table (ID int primary key)
insert into @A values (1)
declare @B table (ID int primary key identity(1,1), B int not null)
insert into @B values (1)
select
[RowCount] = @@RowCount,
[@@IDENTITY] = @@IDENTITY,
[SCOPE\_IDENTITY] = SCOPE\_IDENTITY()
set statistics profile on
insert into \_ddr\_T
output inserted.ID into @MyNewIdentityValues
select
b.ID
from @A a
left join @B b on b.ID = 1
left join @B b2 on b2.B = -1
left join \_ddr\_T t on t.T = -1
where not exists (select \* from \_ddr\_T t2 where t2.ID = -1)
set statistics profile off
select
[RowCount] = @@RowCount,
[@@IDENTITY] = @@IDENTITY,
[SCOPE\_IDENTITY] = SCOPE\_IDENTITY(),
[IDENT\_CURRENT] = IDENT\_CURRENT('\_ddr\_T')
select \* from @MyNewIdentityValues
go
Jeśli w Twojej sytuacji konieczne jest użycie jednej z tych funkcji, możesz użyć jednej z poniższych metod w celu obejścia tego problemu.
Metoda 1.
Uwzględnij następującą opcję w zapytaniu
OPTION (MAXDOP 1)
Uwaga: Może to mieć negatywny wpływ na wydajność SELECT części zapytania.
Metoda 2.
Wczytaj wartość z SELECT części do zestawu zmiennych (lub pojedynczej zmiennej tabeli), a następnie wstaw do tabeli docelowej za pomocą MAXDOP=1. Ponieważ plan INSERT nie będzie równoległy, uzyskasz właściwą semantykę, ale Twój SELECT będzie równoległy, aby osiągnąć pożądaną wydajność.
Metoda 3:
Uruchom następującą instrukcję, aby ustawić opcję maksymalnego stopnia równoległości na 1:
sp\_configure 'max degree of parallelism', 1
go
reconfigure with override
go
Uwaga: ta metoda może spowodować spadek wydajności na serwerze. Nie należy używać tej metody, jeśli nie została ona oceniona w środowisku testowym lub przejściowym.