Qual è il comportamento effettivo del livello di compatibilità 80?


47

Qualcuno potrebbe fornirmi una visione migliore della funzionalità della modalità di compatibilità? Si sta comportando diversamente da quanto mi aspettassi.

Per quanto riguarda le modalità di compatibilità, si tratta della disponibilità e del supporto di alcune strutture linguistiche tra le varie versioni di SQL Server.

Non influisce sul funzionamento interno della versione del motore di database. Tenterebbe di impedire l'uso di funzionalità e costrutti che non erano ancora disponibili nelle versioni precedenti.

Ho appena creato un nuovo database con livello di compatibilità 80 in SQL Server 2008 R2. Ha creato una tabella con una singola colonna int e l'ha popolata con poche righe.

Quindi ha eseguito un'istruzione select con una row_number()funzione.

Il mio pensiero era che, poiché la funzione row_number era stata introdotta solo nel 2005, ciò avrebbe generato un errore in modalità compat 80.

Ma con mia sorpresa questo ha funzionato bene. Quindi, sicuramente, le regole di compatibilità vengono valutate solo dopo aver "salvato qualcosa". Quindi ho creato un proc memorizzato per la mia istruzione row_number.

La creazione del proc memorizzata è andata bene e posso eseguirla perfettamente e ottenere risultati.

Qualcuno potrebbe aiutarmi a capire meglio il funzionamento della modalità di compatibilità? La mia comprensione è ovviamente imperfetta.

Risposte:


66

Dai documenti :

Imposta alcuni comportamenti del database in modo che siano compatibili con la versione specificata di SQL Server.
... Il
livello di compatibilità fornisce solo una retrocompatibilità parziale con le versioni precedenti di SQL Server. Utilizzare il livello di compatibilità come aiuto temporaneo alla migrazione per aggirare le differenze di versione nei comportamenti controllati dall'impostazione del livello di compatibilità pertinente.

Nella mia interpretazione, la modalità di compatibilità riguarda il comportamento e l'analisi della sintassi, non per cose come il parser che dice "Ehi, non puoi usare ROW_NUMBER()!" A volte il livello di compatibilità inferiore consente di continuare a cavarsela con la sintassi non più supportata e talvolta ti impedisce di utilizzare nuovi costrutti di sintassi. La documentazione elenca diversi esempi espliciti, ma qui ci sono alcune dimostrazioni:


Passando funzioni integrate come argomenti di funzione

Questo codice funziona nel livello di compatibilità 90+:

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

Ma in 80 produce:

Messaggio 102, livello 15, stato 1
Sintassi errata vicino a '('.

Il problema specifico qui è che in 80 non è consentito passare una funzione incorporata in una funzione. Se vuoi rimanere in 80 modalità di compatibilità, puoi aggirare questo dicendo:

DECLARE @db_id INT = DB_ID();

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

Passando un tipo di tabella a una funzione con valori di tabella

Simile al precedente, è possibile ottenere un errore di sintassi quando si utilizza un TVP e si tenta di passarlo a una funzione con valori di tabella. Funziona con i moderni livelli di compatibilità:

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

Tuttavia, modificare il livello di compatibilità su 80 ed eseguire nuovamente le ultime tre righe; viene visualizzato questo messaggio di errore:

Messaggio 137, livello 16, stato 1, riga 19 È
necessario dichiarare la variabile scalare "@foo".

Non ho davvero una buona soluzione nella parte superiore della mia testa, a parte l'aggiornamento del livello di compatibilità o ottenere i risultati in modo diverso.


Utilizzo di nomi di colonna qualificati in APPLY

In modalità di compatibilità 90 e successive, puoi farlo senza problemi:

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

Tuttavia, in 80 modalità di compatibilità, la colonna qualificata assegnata alla funzione genera un errore di sintassi generico:

Messaggio 102, livello 15, stato 1
Sintassi errata vicino a ".".


ORDER BY di un alias che corrisponde al nome di una colonna

Considera questa query:

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

In 80 modalità di compatibilità, i risultati sono i seguenti:

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

In 90 modalità di compatibilità, i risultati sono abbastanza diversi:

snmuloc_lla      all_columns
stcejbo_lla      all_objects
sretemarap_lla   all_parameters
...

La ragione? Nella modalità di compatibilità 80, il prefisso della tabella viene ignorato completamente, quindi viene ordinato in base all'espressione definita dall'alias SELECTnell'elenco. Nei livelli di compatibilità più recenti, viene considerato il prefisso della tabella, quindi SQL Server utilizzerà effettivamente quella colonna nella tabella (se viene trovata). Se l' ORDER BYalias non viene trovato nella tabella, i livelli di compatibilità più recenti non perdonano così tanto l'ambiguità. Considera questo esempio:

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

Il risultato è ordinato mynamedall'espressione in 80, perché di nuovo il prefisso della tabella viene ignorato, ma in 90 genera questo messaggio di errore:

Messaggio 207, livello 16, stato 1, riga 3
Nome colonna non valido "mio nome".

Questo è tutto spiegato anche nella documentazione :

Quando si associano i riferimenti di colonna ORDER BYnell'elenco alle colonne definite SELECTnell'elenco, le ambiguità delle colonne vengono ignorate e i prefissi di colonna vengono talvolta ignorati. Ciò può causare la restituzione del set di risultati in un ordine imprevisto.

Ad esempio, viene accettata una ORDER BYclausola con una singola colonna in due parti ( <table_alias>.<column>) utilizzata come riferimento a una colonna in un elenco SELECT, ma l'alias di tabella viene ignorato. Considera la seguente query.

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

Quando eseguito, il prefisso di colonna viene ignorato in ORDER BY. L'operazione di ordinamento non si verifica sulla colonna di origine specificata ( x.c1) come previsto; invece si verifica sul derivatoc1colonna definita nella query. Il piano di esecuzione per questa query mostra che i valori per la colonna derivata vengono calcolati prima e poi i valori calcolati vengono ordinati.


ORDINA PER qualcosa che non è nell'elenco SELECT

In 90 modalità di compatibilità non puoi farlo:

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;

Risultato:

Messaggio 104, livello 16, stato 1 Le
voci ORDER BY devono apparire nell'elenco di selezione se l'istruzione contiene un operatore UNION, INTERSECT o EXCEPT.

In 80, tuttavia, è ancora possibile utilizzare questa sintassi.


Unioni esterne vecchie e icky

La modalità 80 consente inoltre di utilizzare la vecchia sintassi del join esterno obsoleta ( *=/=*):

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

In SQL Server 2008/2008 R2, se hai 90 o più, ricevi questo messaggio dettagliato:

Messaggio 4147, livello 15, stato 1
La query utilizza operatori di join esterno non ANSI (" *=" o " =*"). Per eseguire questa query senza modifiche, impostare il livello di compatibilità per il database corrente su 80, utilizzando l'opzione SET COMPATIBILITY_LEVEL di ALTER DATABASE. Si consiglia vivamente di riscrivere la query utilizzando gli operatori ANSI di join esterno (LEFT OUTER JOIN, RIGHT OUTER JOIN). Nelle versioni future di SQL Server, gli operatori di join non ANSI non saranno supportati nemmeno nelle modalità di retrocompatibilità.

In SQL Server 2012, questa non è più una sintassi valida e produce quanto segue:

Messaggio 102, livello 15, stato 1, riga 3
Sintassi errata vicino a '* ='.

Ovviamente in SQL Server 2012 non è più possibile aggirare questo problema utilizzando il livello di compatibilità, poiché 80 non è più supportato. Se si aggiorna un database in modalità 80 compatibilità (tramite aggiornamento sul posto, scollegamento / collegamento, backup / ripristino, invio log, mirroring, ecc.) Verrà automaticamente aggiornato a 90.


Suggerimenti per la tabella senza CON

Nella modalità 80 compat, è possibile utilizzare quanto segue e verrà osservato il suggerimento tabella:

SELECT * FROM dbo.whatever NOLOCK; 

In 90+, questo NOLOCKnon è più un suggerimento per la tabella, è un alias. Altrimenti, questo funzionerebbe:

SELECT * FROM dbo.whatever AS w NOLOCK;

Ma non:

Messaggio 1018, livello 15, stato 1
Sintassi errata vicino a "NOLOCK". Se questo è inteso come parte di un suggerimento per la tabella, sono ora necessari una parola chiave WITH e una parentesi. Vedere la documentazione in linea di SQL Server per la sintassi corretta.

Ora, per dimostrare che il comportamento non viene osservato nel primo esempio in modalità 90 compat, utilizzare AdventureWorks (assicurandosi che sia a un livello di compatibilità superiore) ed eseguire quanto segue:

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;

Questo è particolarmente problematico perché il comportamento cambia senza un messaggio di errore o addirittura un errore. Ed è anche qualcosa che il consulente per l'aggiornamento e altri strumenti potrebbero non individuare nemmeno, dal momento che, per quanto ne sa, è un alias di tabella.


Conversioni che coinvolgono nuovi tipi di data / ora

I nuovi tipi di data / ora introdotti in SQL Server 2008 (ad es. dateE datetime2) supportano un intervallo molto più ampio rispetto all'originale datetimee smalldatetime). Le conversioni esplicite di valori al di fuori dell'intervallo supportato avranno esito negativo, indipendentemente dal livello di compatibilità, ad esempio:

SELECT CONVERT(SMALLDATETIME, '00010101');

I rendimenti:

Messaggio 242, livello 16, stato 3
La conversione di un tipo di dati varchar in un tipo di dati smalldatetime ha prodotto un valore fuori intervallo.

Tuttavia, le conversioni implicite si risolveranno nei livelli di compatibilità più recenti. Ad esempio, questo funzionerà in 100+:

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

Ma in 80 (e anche in 90), produce un errore simile al precedente:

Messaggio 242, livello 16, stato 3
La conversione di un tipo di dati varchar in un tipo di dati datetime ha prodotto un valore fuori intervallo.


Clausole FOR ridondanti nei trigger

Questo è uno scenario oscuro che è venuto qui . In 80 modalità di compatibilità, ciò avrà successo:

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

In 90 compatibilità e versioni successive, questo non analizza più e invece viene visualizzato il seguente messaggio di errore:

Messaggio 1034, livello 15, stato 1, procedura tx
Errore di sintassi: specifica duplicata dell'azione "AGGIORNAMENTO" nella dichiarazione del trigger.


PIVOT / UNPIVOT

Alcune forme di sintassi non funzioneranno sotto gli 80 (ma funzionano bene in 90+):

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

Questo produce:

Messaggio 156, livello 15, stato 1
Sintassi errata vicino alla parola chiave "per".

Per alcune soluzioni alternative, tra cui CROSS APPLY, vedere queste risposte .


Nuove funzioni integrate

Prova a utilizzare nuove funzioni come TRY_CONVERT()in un database con livello di compatibilità <110. Semplicemente non vengono riconosciute lì.

SELECT TRY_CONVERT(INT, 1);

Risultato:

Messaggio 195, livello 15, stato 10
"TRY_CONVERT" non è un nome di funzione incorporata riconosciuto.


Raccomandazione

Utilizzare la modalità di compatibilità 80 solo se effettivamente necessaria. Dal momento che non sarà più disponibile nella prossima versione dopo il 2008 R2, l'ultima cosa che vuoi fare è scrivere il codice in questo livello di compatibilità, fare affidamento sui comportamenti che vedi e quindi avere un sacco di rotture quando non puoi più usa quel livello di compatibilità. Sii lungimirante e non provare a dipingerti in un angolo acquistando tempo per continuare a utilizzare la sintassi obsoleta e obsoleta.


1
Chiaramente questa è una risposta migliore della mia!
Max Vernon,

Grazie mille per questa risposta elaborata, Aaron! E per aver corretto i miei numerosi errori di ortografia.
souplex,

1
Le note sulla compatibilità di SQL Server 2014 sono disponibili qui: msdn.microsoft.com/en-us/library/bb510680(v=sql.120).aspx
Josh Gallagher,

9

I livelli di compatibilità sono presenti solo per consentire una migrazione controllata da una versione precedente di SQL Server. Compat Level 90 non preclude l'utilizzo di nuove funzionalità, significa semplicemente che alcuni aspetti del database vengono mantenuti in modo compatibile con il funzionamento di SQL Server 2005.

Vedere http://msdn.microsoft.com/en-us/library/bb510680.aspx per ulteriori informazioni.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.