Wie ist das tatsächliche Verhalten der Kompatibilitätsstufe 80?

47

Könnte mir jemand einen besseren Einblick in die Kompatibilitätsmodus-Funktion geben? Es verhält sich anders als ich erwartet hatte.

Soweit ich Kompatibilitätsmodi verstehe, geht es um die Verfügbarkeit und Unterstützung bestimmter Sprachstrukturen zwischen den verschiedenen Versionen von SQL Server.

Dies hat keine Auswirkungen auf das Innenleben der Datenbankmodulversion. Es wird versucht, die Verwendung von Features und Konstrukten zu verhindern, die in früheren Versionen noch nicht verfügbar waren.

Ich habe gerade eine neue Datenbank mit Kompatibilitätsstufe 80 in SQL Server 2008 R2 erstellt. Erstellt eine Tabelle mit einer einzelnen int-Spalte und füllt sie mit einigen Zeilen.

Führen Sie dann eine select-Anweisung mit einer row_number()Funktion aus.

Ich dachte, da die Funktion row_number erst 2005 eingeführt wurde, würde dies einen Fehler im kompatiblen 80-Modus auslösen.

Aber zu meiner Überraschung hat das gut funktioniert. Dann werden die Kompatibilitätsregeln sicherlich erst ausgewertet, wenn Sie "etwas speichern". Also habe ich ein gespeichertes Proc für meine row_number-Anweisung erstellt.

Die gespeicherte Proc-Erstellung verlief einwandfrei und ich kann sie perfekt ausführen und Ergebnisse erzielen.

Könnte mir jemand helfen, die Funktionsweise des Kompatibilitätsmodus besser zu verstehen? Mein Verständnis ist offensichtlich fehlerhaft.

souplex
quelle

Antworten:

66

Aus den Dokumenten :

Legt fest, dass bestimmte Datenbankverhalten mit der angegebenen Version von SQL Server kompatibel sind.
... Die
Kompatibilitätsstufe bietet nur teilweise Abwärtskompatibilität mit früheren Versionen von SQL Server. Verwenden Sie die Kompatibilitätsstufe als vorläufige Migrationshilfe, um Versionsunterschiede in den Verhaltensweisen zu umgehen, die von der jeweiligen Einstellung der Kompatibilitätsstufe gesteuert werden.

In meiner Interpretation geht es im Kompatibilitätsmodus um das Verhalten und Parsen der Syntax, nicht für Dinge wie den Parser, die sagen: "Hey, du kannst nicht verwenden ROW_NUMBER()!" Manchmal können Sie mit der niedrigeren Kompatibilitätsstufe fortfahren, wenn die Syntax nicht mehr unterstützt wird, und manchmal können Sie keine neuen Syntaxkonstrukte mehr verwenden. In der Dokumentation werden einige explizite Beispiele aufgeführt, aber hier sind einige Demonstrationen:


Eingebaute Funktionen als Funktionsargumente übergeben

Dieser Code funktioniert in Kompatibilitätsstufe 90+:

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

Aber in 80 ergibt es:

Meldung 102, Ebene 15, Status 1
Falsche Syntax in der Nähe von '('.

Das spezielle Problem hierbei ist, dass Sie in 80 keine integrierte Funktion an eine Funktion übergeben dürfen. Wenn Sie im Kompatibilitätsmodus 80 bleiben möchten, können Sie dies umgehen, indem Sie sagen:

DECLARE @db_id INT = DB_ID();

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

Übergeben eines Tabellentyps an eine Tabellenwertfunktion

Ähnlich wie oben können Sie einen Syntaxfehler erhalten, wenn Sie einen TVP verwenden und versuchen, ihn an eine Tabellenwertfunktion zu übergeben. Dies funktioniert in modernen Kompatibilitätsstufen:

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);

Ändern Sie jedoch die Kompatibilitätsstufe auf 80 und führen Sie die letzten drei Zeilen erneut aus. Sie erhalten diese Fehlermeldung:

Meldung 137, Ebene 16,
Status 1, Zeile 19 Die skalare Variable "@foo" muss deklariert werden.

Keine wirklich gute Lösung, außer die Kompatibilitätsstufe zu verbessern oder die Ergebnisse auf eine andere Art und Weise zu erhalten.


Verwenden qualifizierter Spaltennamen in APPLY

Im Kompatibilitätsmodus 90 und höher können Sie dies problemlos tun:

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

Im Kompatibilitätsmodus 80 führt die an die Funktion übergebene qualifizierte Spalte jedoch zu einem generischen Syntaxfehler:

Meldung 102, Ebene 15, Status 1
Falsche Syntax in der Nähe von '.'.


ORDER BY ein Alias, der einem Spaltennamen entspricht

Betrachten Sie diese Abfrage:

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

Im 80-Kompatibilitätsmodus lauten die Ergebnisse wie folgt:

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

Im 90-Kompatibilitätsmodus sind die Ergebnisse sehr unterschiedlich:

snmuloc_lla      all_columns
stcejbo_lla      all_objects
sretemarap_lla   all_parameters
...

Der Grund? Im Kompatibilitätsmodus 80 wird das Tabellenpräfix vollständig ignoriert, sodass die Reihenfolge nach dem durch den Alias ​​in der SELECTListe definierten Ausdruck erfolgt . In neueren Kompatibilitätsstufen wird das Tabellenpräfix berücksichtigt, sodass SQL Server diese Spalte in der Tabelle tatsächlich verwendet (sofern sie gefunden wird). Wenn der ORDER BYAlias ​​nicht in der Tabelle gefunden wird, verzeihen die neueren Kompatibilitätsstufen keine Mehrdeutigkeit. Betrachten Sie dieses Beispiel:

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

Das Ergebnis wird nach dem mynameAusdruck in 80 geordnet , da das Tabellenpräfix erneut ignoriert wird, in 90 jedoch die folgende Fehlermeldung generiert wird:

Meldung 207, Ebene 16,
Status 1, Zeile 3 Ungültiger Spaltenname 'Mein Name'.

Dies alles wird auch in der Dokumentation erklärt :

Wenn Sie die Spaltenverweise in der ORDER BYListe an die in der SELECTListe definierten Spalten binden , werden Spaltenmehrdeutigkeiten ignoriert und Spaltenpräfixe manchmal ignoriert. Dies kann dazu führen, dass die Ergebnismenge in einer unerwarteten Reihenfolge zurückgegeben wird.

Eine ORDER BYKlausel mit einer einzelnen zweiteiligen column ( <table_alias>.<column>), die als Verweis auf eine Spalte in einer SELECT-Liste verwendet wird, wird beispielsweise akzeptiert, der Tabellenalias wird jedoch ignoriert. Betrachten Sie die folgende Abfrage.

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

Bei der Ausführung wird das Spaltenpräfix in der ignoriert ORDER BY. Der Sortiervorgang wird nicht x.c1wie erwartet für die angegebene Quellenspalte ( ) ausgeführt. stattdessen tritt es auf der abgeleitetenc1Spalte, die in der Abfrage definiert ist. Der Ausführungsplan für diese Abfrage zeigt, dass zuerst die Werte für die abgeleitete Spalte berechnet und dann die berechneten Werte sortiert werden.


ORDER BY etwas nicht in der SELECT-Liste

Im 90-Kompatibilitätsmodus können Sie dies nicht tun:

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;

Ergebnis:

Nachricht 104, Ebene 16
, Status 1 ORDER BY-Elemente müssen in der Auswahlliste angezeigt werden, wenn die Anweisung einen UNION-, INTERSECT- oder EXCEPT-Operator enthält.

In 80 können Sie diese Syntax jedoch weiterhin verwenden.


Alte, eklige äußere Fugen

Im 80-Modus können Sie auch die alte, veraltete Outer-Join-Syntax ( *=/=*) verwenden:

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

Wenn Sie in SQL Server 2008/2008 R2 mindestens 90 Jahre alt sind, erhalten Sie die folgende ausführliche Meldung:

Meldung 4147, Ebene 15, Status 1
Die Abfrage verwendet Nicht-ANSI-Outer-Join-Operatoren (" *=" oder " =*"). Um diese Abfrage ohne Änderung auszuführen, legen Sie die Kompatibilitätsstufe für die aktuelle Datenbank mit der Option SET COMPATIBILITY_LEVEL von ALTER DATABASE auf 80 fest. Es wird dringend empfohlen, die Abfrage mit ANSI-Outer-Join-Operatoren (LEFT OUTER JOIN, RIGHT OUTER JOIN) neu zu schreiben. In zukünftigen Versionen von SQL Server werden Nicht-ANSI-Verknüpfungsoperatoren auch in Abwärtskompatibilitätsmodi nicht unterstützt.

In SQL Server 2012 ist diese Syntax nicht mehr gültig und führt zu folgenden Ergebnissen:

Meldung 102, Ebene 15, Status 1, Zeile 3
Falsche Syntax in der Nähe von '* ='.

In SQL Server 2012 können Sie dieses Problem natürlich nicht mehr mithilfe der Kompatibilitätsstufe umgehen, da 80 nicht mehr unterstützt wird. Wenn Sie eine Datenbank im 80-Kompatibilitätsmodus aktualisieren (durch direktes Upgrade, Trennen / Anhängen, Sichern / Wiederherstellen, Protokollversand, Spiegeln usw.), wird diese automatisch auf 90 für Sie aktualisiert.


Tabellenhinweise ohne WITH

Im Kompatibilitätsmodus 80 können Sie Folgendes verwenden, und der Tabellenhinweis wird beachtet:

SELECT * FROM dbo.whatever NOLOCK; 

Ab 90 NOLOCKist das kein Tabellenhinweis mehr, sondern ein Alias. Andernfalls würde dies funktionieren:

SELECT * FROM dbo.whatever AS w NOLOCK;

Aber das tut es nicht:

Meldung 1018, Ebene 15,
Status 1 Falsche Syntax in der Nähe von 'NOLOCK'. Wenn dies als Teil eines Tabellenhinweises gedacht ist, sind jetzt ein WITH-Schlüsselwort und eine Klammer erforderlich. Informationen zur richtigen Syntax finden Sie in der SQL Server-Onlinedokumentation.

Um zu beweisen, dass das Verhalten im ersten Beispiel im 90-Kompatibilitätsmodus nicht beobachtet wird, verwenden Sie AdventureWorks (stellen Sie sicher, dass es sich auf einer höheren Kompatibilitätsstufe befindet) und führen Sie Folgendes aus:

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;

Dies ist besonders problematisch, da sich das Verhalten ohne eine Fehlermeldung oder sogar einen Fehler ändert. Und es ist auch etwas, das der Upgrade-Berater und andere Tools möglicherweise nicht einmal erkennen, da es sich, soweit bekannt, um einen Tabellenalias handelt.


Konvertierungen mit neuen Datums- / Zeittypen

Die in SQL Server 2008 eingeführten neuen Datums- / Uhrzeit-Typen (z. B. dateund datetime2) unterstützen einen viel größeren Bereich als das ursprüngliche datetimeund smalldatetime. Explizite Konvertierungen von Werten außerhalb des unterstützten Bereichs schlagen fehl, unabhängig von der Kompatibilitätsstufe. Beispiel:

SELECT CONVERT(SMALLDATETIME, '00010101');

Erträge:

Meldung 242, Ebene 16,
Status 3 Die Konvertierung eines varchar-Datentyps in einen smalldatetime-Datentyp führte zu einem Wert außerhalb des Bereichs.

Implizite Konvertierungen funktionieren jedoch in den neueren Kompatibilitätsstufen. Zum Beispiel wird dies in 100+ funktionieren:

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

Aber in 80 (und auch in 90) ergibt sich ein ähnlicher Fehler wie oben:

Meldung 242, Ebene 16,
Status 3 Die Konvertierung eines varchar-Datentyps in einen datetime-Datentyp führte zu einem Wert außerhalb des zulässigen Bereichs.


Redundante FOR-Klauseln in Triggern

Dies ist ein obskures Szenario, das hier aufgetaucht ist . Im Kompatibilitätsmodus 80 ist dies erfolgreich:

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

Bei Kompatibilität mit 90 und höher wird dies nicht mehr analysiert, und stattdessen wird die folgende Fehlermeldung angezeigt:

Meldung 1034, Ebene 15, Status 1, Prozedur tx
Syntaxfehler: Doppelte Angabe der Aktion "UPDATE" in der Triggerdeklaration.


PIVOT / UNPIVOT

Einige Syntaxformen funktionieren nicht unter 80 (aber gut in 90+):

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

Dies ergibt:

Meldung 156, Ebene 15, Status 1
Falsche Syntax neben dem Schlüsselwort 'for'.

Für einige Abhilfen, einschließlich CROSS APPLYfinden diese Antworten .


Neue eingebaute Funktionen

Versuchen Sie, neue Funktionen wie TRY_CONVERT()in einer Datenbank mit einem Kompatibilitätsgrad <110 zu verwenden. Sie werden dort einfach überhaupt nicht erkannt.

SELECT TRY_CONVERT(INT, 1);

Ergebnis:

Meldung 195, Ebene 15,
Status 10 'TRY_CONVERT' ist kein erkannter Name für die integrierte Funktion.


Empfehlung

Verwenden Sie den Kompatibilitätsmodus 80 nur, wenn Sie ihn tatsächlich benötigen. Da es in der nächsten Version nach 2008 R2 nicht mehr verfügbar ist, müssen Sie als Letztes Code in dieser Kompatibilitätsstufe schreiben, sich auf das Verhalten verlassen, das Sie sehen, und dann eine ganze Reihe von Fehlern haben, wenn Sie dies nicht mehr können benutze diesen kompatiblen Level. Denken Sie vorausschauend und versuchen Sie nicht, sich selbst in eine Ecke zu malen, indem Sie sich Zeit nehmen, um weiterhin alte, veraltete Syntax zu verwenden.

Aaron Bertrand
quelle
1
Das ist eindeutig eine bessere Antwort als meine!
Max Vernon
Vielen Dank für diese ausführliche Antwort, Aaron! Und zur Behebung meiner zahlreichen Rechtschreibfehler.
souplex
1
Hinweise zur SQL Server 2014-Kompatibilität finden Sie hier: msdn.microsoft.com/en-us/library/bb510680(v=sql.120).aspx
Josh Gallagher
9

Kompatibilitätsstufen sind nur vorhanden, um eine kontrollierte Migration von einer früheren Version von SQL Server zu ermöglichen. Compat Level 90 schließt die Verwendung neuer Funktionen nicht aus, sondern bedeutet lediglich, dass bestimmte Aspekte der Datenbank auf eine Weise beibehalten werden, die mit der Funktionsweise von SQL Server 2005 kompatibel ist.

Weitere Informationen finden Sie unter http://msdn.microsoft.com/en-us/library/bb510680.aspx .

Max Vernon
quelle