¿Cuál es el comportamiento real del nivel de compatibilidad 80?

47

¿Podría alguien proporcionarme una mejor idea sobre la función del modo de compatibilidad? Se está comportando diferente de lo que esperaba.

Hasta donde entiendo los modos de compatibilidad, se trata de la disponibilidad y el soporte de ciertas estructuras de lenguaje entre las diversas versiones de SQL Server.

No afecta el funcionamiento interno de la versión del motor de base de datos. Trataría de evitar el uso de características y construcciones que aún no estaban disponibles en versiones anteriores.

Acabo de crear una nueva base de datos con nivel de compatibilidad 80 en SQL Server 2008 R2. Creó una tabla con una sola columna int y la pobló con unas pocas filas.

Luego ejecutó una instrucción select con una row_number()función.

Pensé que, dado que la función row_number solo se introdujo en 2005, esto arrojaría un error en el modo compat 80.

Pero para mi sorpresa, esto funcionó bien. Entonces, seguramente, las reglas de compatibilidad solo se evalúan una vez que 'guarda algo'. Así que creé un proceso almacenado para mi declaración row_number.

La creación del proceso almacenado salió bien y puedo ejecutarla perfectamente y obtener resultados.

¿Podría alguien ayudarme a comprender mejor el funcionamiento del modo de compatibilidad? Mi comprensión es obviamente defectuosa.

souplex
fuente

Respuestas:

66

De los documentos :

Establece ciertos comportamientos de la base de datos para que sean compatibles con la versión especificada de SQL Server.
... El
nivel de compatibilidad solo proporciona compatibilidad parcial con versiones anteriores de SQL Server. Utilice el nivel de compatibilidad como ayuda de migración provisional para solucionar las diferencias de versión en los comportamientos controlados por la configuración de nivel de compatibilidad relevante.

En mi interpretación, el modo de compatibilidad se trata del comportamiento y el análisis de la sintaxis, no para cosas como el analizador que dice: "¡Hey, no puedes usarlo ROW_NUMBER()!" A veces, el nivel de compatibilidad más bajo le permite continuar evitando que la sintaxis ya no sea compatible, y a veces le impide usar nuevas construcciones de sintaxis. La documentación enumera varios ejemplos explícitos, pero aquí hay algunas demostraciones:


Pasar funciones integradas como argumentos de función

Este código funciona en el nivel de compatibilidad 90+:

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

Pero en 80 produce:

Mensaje 102, Nivel 15, Estado 1
Sintaxis incorrecta cerca de '('.

El problema específico aquí es que en 80 no se le permite pasar una función incorporada a una función. Si desea permanecer en el modo de compatibilidad 80, puede solucionar esto diciendo:

DECLARE @db_id INT = DB_ID();

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

Pasar un tipo de tabla a una función con valores de tabla

Similar a lo anterior, puede obtener un error de sintaxis al usar un TVP e intentar pasarlo a una función con valores de tabla. Esto funciona en niveles de compatibilidad modernos:

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

Sin embargo, cambie el nivel de compatibilidad a 80 y ejecute las últimas tres líneas nuevamente; obtienes este mensaje de error:

Mensaje 137, Nivel 16, Estado 1, Línea 19
Debe declarar la variable escalar "@foo".

En realidad, no es una buena solución alternativa, aparte de actualizar el nivel de compatibilidad u obtener los resultados de una manera diferente.


Usar nombres de columna calificados en APLICAR

En el modo de compatibilidad 90 y superior, puede hacer esto sin problema:

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

Sin embargo, en el modo de compatibilidad 80, la columna calificada entregada a la función genera un error de sintaxis genérico:

Mensaje 102, Nivel 15, Estado 1
Sintaxis incorrecta cerca de '.'.


ORDER BY un alias que coincide con el nombre de una columna

Considere esta consulta:

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

En el modo de compatibilidad 80, los resultados son los siguientes:

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

En el modo de compatibilidad 90, los resultados son bastante diferentes:

snmuloc_lla      all_columns
stcejbo_lla      all_objects
sretemarap_lla   all_parameters
...

¿La razón? En el modo de compatibilidad 80, el prefijo de la tabla se ignora por completo, por lo que está ordenado por la expresión definida por el alias en la SELECTlista. En los niveles de compatibilidad más nuevos, se considera el prefijo de la tabla, por lo que SQL Server realmente usará esa columna en la tabla (si se encuentra). Si el ORDER BYalias no se encuentra en la tabla, los niveles de compatibilidad más nuevos no son tan indulgentes con la ambigüedad. Considere este ejemplo:

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

El resultado está ordenado por la mynameexpresión en 80, porque nuevamente se ignora el prefijo de la tabla, pero en 90 genera este mensaje de error:

Mensaje 207, Nivel 16, Estado 1, Línea 3
Nombre de columna no válido 'myname'.

Todo esto también se explica en la documentación :

Cuando se vinculan las referencias de columna en la ORDER BYlista a las columnas definidas en la SELECTlista, se ignoran las ambigüedades de columna y los prefijos de columna a veces se ignoran. Esto puede hacer que el conjunto de resultados regrese en un orden inesperado.

Por ejemplo, se acepta una ORDER BYcláusula con una sola columna de dos partes ( <table_alias>.<column>) que se usa como referencia a una columna en una lista SELECT, pero se ignora el alias de la tabla. Considere la siguiente consulta.

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

Cuando se ejecuta, el prefijo de columna se ignora en el ORDER BY. La operación de clasificación no se produce en la columna fuente especificada ( x.c1) como se esperaba; en cambio ocurre en el derivadoc1columna que se define en la consulta. El plan de ejecución para esta consulta muestra que los valores de la columna derivada se calculan primero y luego se ordenan los valores calculados.


ORDER BY algo que no está en la lista SELECT

En el modo de compatibilidad 90 no puedes hacer esto:

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;

Resultado:

Los elementos Msg 104, Nivel 16, Estado 1
ORDER BY deben aparecer en la lista de selección si la declaración contiene un operador UNION, INTERSECT o EXCEPT.

Sin embargo, en 80 todavía puede usar esta sintaxis.


Uniones exteriores viejas y repulsivas

El modo 80 también le permite usar la antigua y obsoleta sintaxis de combinación externa ( *=/=*):

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

En SQL Server 2008/2008 R2, si tiene 90 o más, recibirá este mensaje detallado:

Mensaje 4147, Nivel 15, Estado 1
La consulta utiliza operadores de combinación externa no ANSI (" *=" o " =*"). Para ejecutar esta consulta sin modificaciones, establezca el nivel de compatibilidad para la base de datos actual en 80, utilizando la opción SET COMPATIBILITY_LEVEL de ALTER DATABASE. Se recomienda volver a escribir la consulta utilizando operadores de combinación externa ANSI (UNIDAD EXTERIOR IZQUIERDA, UNIÓN EXTERIOR DERECHA) En las futuras versiones de SQL Server, los operadores de unión que no sean ANSI no serán compatibles incluso en los modos de compatibilidad con versiones anteriores.

En SQL Server 2012, esto ya no es una sintaxis válida y produce lo siguiente:

Mensaje 102, Nivel 15, Estado 1, Línea 3
Sintaxis incorrecta cerca de '* ='.

Por supuesto, en SQL Server 2012 ya no puede solucionar este problema utilizando el nivel de compatibilidad, ya que 80 ya no es compatible. Si actualiza una base de datos en modo de compatibilidad 80 (mediante actualización en el lugar, desconectar / adjuntar, copia de seguridad / restaurar, envío de registros, duplicación, etc.), automáticamente se actualizará a 90 para usted.


Sugerencias de tabla sin CON

En el modo de compatibilidad 80, puede usar lo siguiente y se observará la sugerencia de la tabla:

SELECT * FROM dbo.whatever NOLOCK; 

En 90+, eso NOLOCKya no es una pista de tabla, es un alias. De lo contrario, esto funcionaría:

SELECT * FROM dbo.whatever AS w NOLOCK;

Pero no lo hace:

Mensaje 1018, Nivel 15, Estado 1
Sintaxis incorrecta cerca de 'NOLOCK'. Si esto está pensado como parte de una sugerencia de tabla, ahora se requiere una palabra clave y paréntesis A WITH. Consulte los Libros en pantalla de SQL Server para obtener la sintaxis adecuada.

Ahora, para demostrar que el comportamiento no se observa en el primer ejemplo cuando está en modo de compatibilidad 90, use AdventureWorks (asegurándose de que esté en un nivel de compatibilidad más alto) y ejecute lo siguiente:

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;

Este es particularmente problemático porque el comportamiento cambia sin un mensaje de error o incluso un error. Y también es algo que el asesor de actualizaciones y otras herramientas podrían ni siquiera detectar, ya que, por lo que sabe, es un alias de tabla.


Conversiones que involucran nuevos tipos de fecha / hora

Los nuevos tipos de fecha / hora introducidos en SQL Server 2008 (por ejemplo, datey datetime2) admiten un rango mucho mayor que el original datetimey smalldatetime). Las conversiones explícitas de valores fuera del rango admitido fallarán sin importar el nivel de compatibilidad, por ejemplo:

SELECT CONVERT(SMALLDATETIME, '00010101');

Rendimientos:

Msg 242, Nivel 16, Estado 3
La conversión de un tipo de datos varchar a un tipo de datos smalldatetime dio como resultado un valor fuera de rango.

Sin embargo, las conversiones implícitas se resolverán en los niveles de compatibilidad más nuevos. Por ejemplo, esto funcionará en más de 100:

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

Pero en 80 (y también en 90), produce un error similar al anterior:

Msg 242, Nivel 16, Estado 3
La conversión de un tipo de datos varchar a un tipo de datos datetime dio como resultado un valor fuera de rango.


Cláusulas FOR redundantes en desencadenantes

Este es un escenario oscuro que surgió aquí . En el modo de compatibilidad 80, esto tendrá éxito:

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

En 90 compatibilidad y superior, esto ya no se analiza, y en su lugar aparece el siguiente mensaje de error:

Mensaje 1034, Nivel 15, Estado 1, Procedimiento tx
Error de sintaxis: especificación duplicada de la acción "ACTUALIZAR" en la declaración de activación.


PIVOTE / UNPIVOT

Algunas formas de sintaxis no funcionarán por debajo de 80 (pero funcionan bien en 90+):

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

Esto produce:

Mensaje 156, Nivel 15, Estado 1
Sintaxis incorrecta cerca de la palabra clave 'para'.

Para algunas soluciones, incluso CROSS APPLY, vea estas respuestas .


Nuevas funciones integradas

Intente usar nuevas funciones como TRY_CONVERT()en una base de datos con un nivel de compatibilidad <110. Simplemente no se reconocen allí en absoluto.

SELECT TRY_CONVERT(INT, 1);

Resultado:

Mensaje 195, Nivel 15, Estado 10
'TRY_CONVERT' no es un nombre de función incorporado reconocido.


Recomendación

Solo use el modo de compatibilidad 80 si realmente lo necesita. Dado que ya no estará disponible en la próxima versión después de 2008 R2, lo último que desea hacer es escribir código en este nivel de compatibilidad, confiar en los comportamientos que ve y luego tener un montón de roturas cuando ya no pueda usa ese nivel de compatibilidad. Sea progresista y no intente pintarse en una esquina al ganar tiempo para seguir usando una sintaxis antigua y obsoleta.

Aaron Bertrand
fuente
1
¡Claramente esa es una mejor respuesta que la mía!
Max Vernon
¡Muchas gracias por esta elaborada respuesta, Aaron! Y por arreglar mis numerosos errores ortográficos.
souplex
1
Las notas de compatibilidad de SQL Server 2014 se encuentran aquí: msdn.microsoft.com/en-us/library/bb510680(v=sql.120).aspx
Josh Gallagher
9

Los niveles de compatibilidad solo están presentes para permitir una migración controlada desde una versión anterior de SQL Server. Compat Level 90 no impide el uso de nuevas funciones, simplemente significa que ciertos aspectos de la base de datos se conservan de una manera compatible con el funcionamiento de SQL Server 2005.

Consulte http://msdn.microsoft.com/en-us/library/bb510680.aspx para obtener más información.

Max Vernon
fuente