Jakie jest faktyczne zachowanie poziomu zgodności 80?

47

Czy ktoś mógłby mi zapewnić lepszy wgląd w funkcję trybu zgodności? Zachowuje się inaczej niż się spodziewałem.

O ile rozumiem tryby zgodności, chodzi o dostępność i obsługę niektórych struktur językowych między różnymi wersjami SQL Server.

Nie wpływa to na wewnętrzne działanie wersji silnika bazy danych. Próbowałby zapobiec wykorzystaniu funkcji i konstrukcji, które nie były jeszcze dostępne we wcześniejszych wersjach.

Właśnie utworzyłem nową bazę danych z poziomem zgodności 80 w SQL Server 2008 R2. Utworzył tabelę z pojedynczą kolumną int i zapełnił ją kilkoma wierszami.

Następnie wykonał instrukcję select z row_number()funkcją.

Myślałem, że ponieważ funkcja row_number została wprowadzona dopiero w 2005 r., Spowodowałoby to błąd w trybie zgodnym z 80.

Ale ku mojemu zaskoczeniu zadziałało dobrze. Z pewnością reguły zgodności są oceniane dopiero po „zapisaniu czegoś”. Więc stworzyłem zapisany proc dla mojej instrukcji row_number.

Zapisane tworzenie proc poszło dobrze i mogę go doskonale wykonać i uzyskać wyniki.

Czy ktoś mógłby mi pomóc lepiej zrozumieć działanie trybu zgodności? Moje zrozumienie jest oczywiście błędne.

souplex
źródło

Odpowiedzi:

66

Z dokumentów :

Ustawia pewne zachowania bazy danych, aby były zgodne z określoną wersją programu SQL Server.
...
Poziom zgodności zapewnia tylko częściową zgodność wsteczną z wcześniejszymi wersjami SQL Server. Użyj poziomu zgodności jako tymczasowej pomocy w migracji, aby obejść różnice między wersjami w zachowaniach kontrolowanych przez odpowiednie ustawienie poziomu zgodności.

W mojej interpretacji tryb zgodności dotyczy zachowania i analizowania składni, a nie takich rzeczy jak parser mówiący: „Hej, nie możesz użyć ROW_NUMBER()!” Czasami niższy poziom zgodności pozwala na dalsze unikanie składni, która nie jest już obsługiwana, a czasami uniemożliwia korzystanie z nowych konstrukcji składni. Dokumentacja zawiera kilka wyraźnych przykładów, ale oto kilka demonstracji:


Przekazywanie wbudowanych funkcji jako argumentów funkcji

Ten kod działa na poziomie zgodności 90+:

SELECT *
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, NULL);

Ale w 80 daje:

Msg 102, poziom 15, stan 1
Niepoprawna składnia w pobliżu „(”.

Szczególny problem polega na tym, że w 80 nie można przekazać funkcji wbudowanej w funkcję. Jeśli chcesz pozostać w trybie zgodności 80, możesz obejść ten problem, mówiąc:

DECLARE @db_id INT = DB_ID();

SELECT * 
FROM sys.dm_db_index_physical_stats(@db_id, NULL, NULL, NULL, NULL);

Przekazywanie typu tabeli do funkcji o wartości tabeli

Podobnie do powyższego, możesz otrzymać błąd składniowy podczas używania TVP i próby przekazania go do funkcji o wartości tabeli. Działa to na współczesnych poziomach kompatybilności:

CREATE TYPE dbo.foo AS TABLE(bar INT);
GO
CREATE FUNCTION dbo.whatever
(
  @foo dbo.foo READONLY
)
RETURNS TABLE
AS 
  RETURN (SELECT bar FROM @foo);
GO

DECLARE @foo dbo.foo;
INSERT @foo(bar) SELECT 1;
SELECT * FROM dbo.whatever(@foo);

Zmień jednak poziom zgodności na 80 i ponownie uruchom trzy ostatnie linie; pojawia się ten komunikat o błędzie:

Msg 137, poziom 16, stan 1, wiersz 19
Musi zadeklarować zmienną skalarną „@foo”.

Naprawdę nie ma dobrego obejścia poza szczytem mojej głowy, poza podniesieniem poziomu zgodności lub uzyskaniem wyników w inny sposób.


Używanie kwalifikowanych nazw kolumn w ZASTOSUJ

W trybie zgodności 90 i nowszym możesz to zrobić bez problemu:

SELECT * FROM sys.dm_exec_cached_plans AS p
  CROSS APPLY sys.dm_exec_sql_text(p.plan_handle) AS t;

Jednak w trybie zgodności 80 kolumna kwalifikowana przekazana do funkcji wywołuje ogólny błąd składniowy:

Msg 102, poziom 15, stan 1
Niepoprawna składnia w pobliżu „.”.


ORDER BY alias, który pasuje do nazwy kolumny

Rozważ to zapytanie:

SELECT name = REVERSE(name), realname = name 
FROM sys.all_objects AS o
ORDER BY o.name;

W trybie zgodności 80 wyniki są następujące:

001_ofni_epytatad_ps   sp_datatype_info_100
001_scitsitats_ps      sp_statistics_100
001_snmuloc_corps_ps   sp_sproc_columns_100
...

W trybie zgodności 90 wyniki są zupełnie inne:

snmuloc_lla      all_columns
stcejbo_lla      all_objects
sretemarap_lla   all_parameters
...

Powód? W trybie zgodności 80 prefiks tabeli jest całkowicie ignorowany, więc jest uporządkowany według wyrażenia zdefiniowanego przez alias na SELECTliście. W nowszych poziomach zgodności brany jest pod uwagę prefiks tabeli, więc SQL Server faktycznie użyje tej kolumny w tabeli (jeśli zostanie znaleziona). Jeśli ORDER BYw tabeli nie znaleziono aliasu, nowsze poziomy zgodności nie wybaczają tak bardzo dwuznaczności. Rozważ ten przykład:

SELECT myname = REVERSE(name), realname = name 
FROM sys.all_objects AS o
ORDER BY o.myname;

Wynik jest uporządkowany według mynamewyrażenia w 80, ponieważ ponownie prefiks tabeli jest ignorowany, ale w 90 generuje ten komunikat o błędzie:

Msg 207, poziom 16, stan 1, wiersz 3
Niepoprawna nazwa kolumny „moja nazwa”.

Wszystko to wyjaśniono również w dokumentacji :

Podczas wiązania odniesień ORDER BYdo kolumn na liście z kolumnami zdefiniowanymi na SELECTliście, niejednoznaczności kolumn są ignorowane, a prefiksy kolumn są czasami ignorowane. Może to spowodować, że zestaw wyników zwróci się w nieoczekiwanej kolejności.

Na przykład ORDER BYklauzula z pojedynczą dwuczęściową kolumną ( <table_alias>.<column>), która jest używana jako odwołanie do kolumny na liście SELECT, jest akceptowana, ale alias tabeli jest ignorowany. Rozważ następujące zapytanie.

SELECT c1 = -c1 FROM t_table AS x ORDER BY x.c1

Po uruchomieniu prefiks kolumny jest ignorowany w pliku ORDER BY. Operacja sortowania nie występuje x.c1zgodnie z oczekiwaniami na określonej kolumnie źródłowej ( ); zamiast tego występuje na pochodnejc1kolumna zdefiniowana w zapytaniu. Plan wykonania dla tego zapytania pokazuje, że wartości dla kolumny pochodnej są najpierw obliczane, a następnie wartości obliczane są sortowane.


ZAMÓW PRZEZ coś, czego nie ma na liście WYBIERZ

W trybie zgodności 90 nie można tego zrobić:

SELECT name = COALESCE(a.name, '') FROM sys.objects AS a
UNION ALL
SELECT name = COALESCE(a.name, '') FROM sys.objects AS a
ORDER BY a.name;

Wynik:

Msg 104, poziom 16, stan 1
ORDER BY elementy muszą pojawić się na liście wyboru, jeśli instrukcja zawiera operator UNION, INTERSECT lub EXCEPT.

Jednak w 80 można nadal używać tej składni.


Stare, obskurne połączenia zewnętrzne

Tryb 80 pozwala również na użycie starej, przestarzałej składni sprzężenia zewnętrznego ( *=/=*):

SELECT o.name, c.name
FROM sys.objects AS o, sys.columns AS c
WHERE o.[object_id] *= c.[object_id];

W SQL Server 2008/2008 R2, jeśli masz 90 lat lub więcej, pojawia się następujący komunikat:

Msg 4147, poziom 15, stan 1
W zapytaniu zastosowano zewnętrzne operatory łączenia inne niż ANSI („ *=” lub „ =*”). Aby uruchomić to zapytanie bez modyfikacji, ustaw poziom zgodności dla bieżącej bazy danych na 80, używając opcji SET COMPATIBILITY_LEVEL opcji ALTER DATABASE. Zdecydowanie zaleca się przepisanie zapytania przy użyciu zewnętrznych operatorów złącz ANSI (POŁĄCZENIE ZEWNĘTRZNE, POŁĄCZENIE ZEWNĘTRZNE). W przyszłych wersjach SQL Server operatory łączenia inne niż ANSI nie będą obsługiwane nawet w trybach zgodności wstecznej.

W SQL Server 2012 w ogóle nie jest to już poprawna składnia i powoduje:

Msg 102, poziom 15, stan 1, wiersz 3
Niepoprawna składnia w pobliżu „* =”.

Oczywiście w SQL Server 2012 nie można już obejść tego problemu przy użyciu poziomu zgodności, ponieważ 80 nie jest już obsługiwany. Jeśli uaktualnisz bazę danych w trybie zgodności z 80 (poprzez uaktualnienie na miejscu, odłączenie / dołączenie, tworzenie kopii zapasowych / przywracanie, wysyłanie dziennika, tworzenie kopii lustrzanych itp.), Zostanie ona automatycznie uaktualniona do 90.


Wskazówki do tabeli bez Z

W trybie kompatybilności z 80 można użyć następujących elementów i przestrzegać podpowiedzi dotyczących tabeli:

SELECT * FROM dbo.whatever NOLOCK; 

W 90+ NOLOCKnie jest to już wskazówka przy stole, to jest alias. W przeciwnym razie zadziałałoby to:

SELECT * FROM dbo.whatever AS w NOLOCK;

Ale to nie:

Msg 1018, poziom 15, stan 1
Niepoprawna składnia w pobliżu „NOLOCK”. Jeśli jest to zamierzone jako podpowiedź do tabeli, słowo kluczowe A WITH i nawiasy są teraz wymagane. Zobacz SQL Server Books Online, aby uzyskać odpowiednią składnię.

Teraz, aby udowodnić, że zachowanie nie jest obserwowane w pierwszym przykładzie w trybie zgodności z 90, użyj AdventureWorks (upewniając się, że jest na wyższym poziomie kompatybilności) i uruchom następujące polecenie:

BEGIN TRANSACTION;
SELECT TOP (1) * FROM Sales.SalesOrderHeader UPDLOCK;
SELECT * FROM sys.dm_tran_locks 
  WHERE request_session_id = @@SPID
  AND resource_type IN ('KEY', 'OBJECT'); -- how many rows here? 0
COMMIT TRANSACTION;

BEGIN TRANSACTION;
SELECT TOP (1) * FROM Sales.SalesOrderHeader WITH (UPDLOCK);
SELECT * FROM sys.dm_tran_locks
  WHERE request_session_id = @@SPID
  AND resource_type IN ('KEY', 'OBJECT'); -- how many rows here? 2
COMMIT TRANSACTION;

Jest to szczególnie problematyczne, ponieważ zachowanie zmienia się bez komunikatu o błędzie lub nawet błędu. Jest to również coś, czego doradca uaktualnienia i inne narzędzia mogą nawet nie zauważyć, ponieważ jest to alias tabeli.


Konwersje obejmujące nowe typy daty / godziny

Nowe typy daty / godziny wprowadzone w SQL Server 2008 (np. dateI datetime2) obsługują znacznie większy zakres niż oryginalny datetimei smalldatetime). Jawne konwersje wartości poza obsługiwanym zakresem zakończą się niepowodzeniem bez względu na poziom zgodności, na przykład:

SELECT CONVERT(SMALLDATETIME, '00010101');

Wydajność:

Msg 242, poziom 16, stan 3
Konwersja typu danych varchar na typ danych smalldatetime spowodowała powstanie wartości spoza zakresu.

Jednak niejawne konwersje sprawdzą się na nowszych poziomach zgodności. Na przykład będzie to działać w 100+:

SELECT DATEDIFF(DAY, CONVERT(SMALLDATETIME, SYSDATETIME()), '00010101');

Ale w 80 (a także w 90) daje podobny błąd jak powyżej:

Msg 242, poziom 16, stan 3
Konwersja typu danych varchar na typ danych data-godzina spowodowała, że ​​wartość była poza zakresem.


Nadmiarowe klauzule FOR w wyzwalaczach

To jest niejasny scenariusz, który pojawił się tutaj . W trybie zgodności 80 zakończy się to powodzeniem:

CREATE TABLE dbo.x(y INT);
GO
CREATE TRIGGER tx ON dbo.x
FOR UPDATE, UPDATE
------------^^^^^^ notice the redundant UPDATE
AS PRINT 1;

W wersji 90 i wyższej nie jest to już analizowane, a zamiast tego pojawia się następujący komunikat o błędzie:

Msg 1034, poziom 15, stan 1, procedura tx
Błąd składniowy: zduplikowana specyfikacja akcji „UPDATE” w deklaracji wyzwalacza.


PIVOT / UNPIVOT

Niektóre formy składni nie działają poniżej 80 (ale działają dobrze w 90+):

SELECT col1, col2
FROM dbo.t1
UNPIVOT (value FOR col3 IN ([x],[y])) AS p;

Daje to:

Msg 156, poziom 15, stan 1
Niepoprawna składnia w pobliżu słowa kluczowego „for”.

Aby CROSS APPLYzapoznać się z niektórymi obejściami, w tym zobacz te odpowiedzi .


Nowe wbudowane funkcje

Spróbuj użyć nowych funkcji, takich jak TRY_CONVERT()w bazie danych o poziomie zgodności <110. Po prostu w ogóle ich nie rozpoznaje.

SELECT TRY_CONVERT(INT, 1);

Wynik:

Msg 195, poziom 15, stan 10
„TRY_CONVERT” nie jest rozpoznaną nazwą wbudowanej funkcji.


Zalecenie

Używaj trybu zgodności 80 tylko wtedy, gdy go potrzebujesz. Ponieważ nie będzie już dostępny w następnej wersji po 2008 R2, ostatnią rzeczą, którą chcesz zrobić, to napisać kod na tym poziomie kompatybilności, polegać na zachowaniach, które widzisz, a następnie mieć cały szereg zepsucia, gdy nie możesz już użyj tego poziomu zgodności. Myśl dalej i nie próbuj zamalować się w kącie, kupując czas na kontynuowanie starej, przestarzałej składni.

Aaron Bertrand
źródło
1
Oczywiście jest to lepsza odpowiedź niż moja!
Max Vernon,
Dziękuję bardzo za tę szczegółową odpowiedź, Aaron! I za naprawienie moich licznych błędów ortograficznych.
souplex
1
Uwagi dotyczące zgodności z programem SQL Server 2014 można znaleźć tutaj: msdn.microsoft.com/en-us/library/bb510680(v=sql.120).aspx
Josh Gallagher
9

Poziomy zgodności są obecne tylko w celu umożliwienia kontrolowanej migracji z wcześniejszej wersji programu SQL Server. Compat Level 90 nie wyklucza korzystania z nowych funkcji, oznacza to po prostu, że niektóre aspekty bazy danych są przechowywane w sposób zgodny z działaniem SQL Server 2005.

Więcej informacji można znaleźć na stronie http://msdn.microsoft.com/en-us/library/bb510680.aspx .

Max Vernon
źródło