Qual é o comportamento real do nível de compatibilidade 80?

47

Alguém poderia me fornecer uma melhor visão sobre o recurso do modo de compatibilidade? Está se comportando diferente do que eu esperava.

Tanto quanto eu entendo os modos de compatibilidade, trata-se da disponibilidade e suporte de determinadas estruturas de linguagem entre as várias versões do SQL Server.

Não afeta o funcionamento interno da versão do mecanismo de banco de dados. Ele tentaria impedir o uso de recursos e construções que ainda não estavam disponíveis nas versões anteriores.

Acabei de criar um novo banco de dados com o nível 80 compatível no SQL Server 2008 R2. Criou uma tabela com uma única coluna int e a preencheu com algumas linhas.

Em seguida, executou uma instrução select com uma row_number()função

Meu pensamento era que, como a função row_number foi introduzida apenas em 2005, isso geraria um erro no modo compat 80.

Mas, para minha surpresa, isso funcionou bem. Então, certamente, as regras compat são avaliadas apenas quando você 'salva algo'. Então, eu criei um proc armazenado para minha instrução row_number.

A criação do proc armazenado correu bem e eu posso executá-lo perfeitamente e obter resultados.

Alguém poderia me ajudar a entender melhor o funcionamento do modo de compatibilidade? Meu entendimento é obviamente falho.

souplex
fonte

Respostas:

66

Dos documentos :

Define certos comportamentos do banco de dados para serem compatíveis com a versão especificada do SQL Server.
... O
nível de compatibilidade fornece compatibilidade reversa apenas parcial com versões anteriores do SQL Server. Use o nível de compatibilidade como um auxílio provisório à migração para solucionar diferenças de versão nos comportamentos controlados pela configuração relevante no nível de compatibilidade.

Na minha interpretação, o modo de compatibilidade é sobre comportamento e análise de sintaxe, não para coisas como o analisador dizendo: "Ei, você não pode usar ROW_NUMBER()!" Às vezes, o nível de compatibilidade mais baixo permite continuar com a sintaxe que não é mais suportada e, às vezes, impede o uso de novas construções de sintaxe. A documentação lista vários exemplos explícitos, mas aqui estão algumas demonstrações:


Passando funções internas como argumentos de função

Este código funciona no nível de compatibilidade 90+:

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

Mas em 80 ele produz:

Msg 102, Nível 15, Estado 1
Sintaxe incorreta próxima a '('.

O problema específico aqui é que, em 80, você não tem permissão para passar uma função interna para uma função. Se você deseja permanecer no modo de compatibilidade 80, pode solucionar isso dizendo:

DECLARE @db_id INT = DB_ID();

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

Passando um tipo de tabela para uma função com valor de tabela

Semelhante ao acima, você pode obter um erro de sintaxe ao usar um TVP e tentar transmiti-lo para uma função com valor de tabela. Isso funciona nos modernos níveis compat:

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

No entanto, altere o nível de compatibilidade para 80 e execute as três últimas linhas novamente; você recebe esta mensagem de erro:

Mensagem 137, nível 16, estado 1, linha 19
Deve declarar a variável escalar "@foo".

Não é realmente uma boa solução alternativa, além de atualizar o nível de compatibilidade ou obter os resultados de uma maneira diferente.


Usando nomes de colunas qualificados no APPLY

No modo de compatibilidade 90 e superior, você pode fazer isso sem problemas:

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

No entanto, no modo de compatibilidade 80, a coluna qualificada entregue à função gera um erro de sintaxe genérico:

Msg 102, Nível 15, Estado 1
Sintaxe incorreta próxima a '.'.


ORDER BY um alias que coincide com o nome de uma coluna

Considere esta consulta:

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

No modo de compatibilidade 80, os resultados são os seguintes:

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

No modo de compatibilidade 90, os resultados são bem diferentes:

snmuloc_lla      all_columns
stcejbo_lla      all_objects
sretemarap_lla   all_parameters
...

O motivo? No modo de compatibilidade 80, o prefixo da tabela é totalmente ignorado, portanto, é ordenado pela expressão definida pelo alias na SELECTlista. Nos níveis de compatibilidade mais recentes, o prefixo da tabela é considerado; portanto, o SQL Server realmente usará essa coluna na tabela (se for encontrada). Se o ORDER BYalias não for encontrado na tabela, os níveis de compatibilidade mais recentes não perdoam a ambiguidade. Considere este exemplo:

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

O resultado é ordenado pela mynameexpressão em 80, porque novamente o prefixo da tabela é ignorado, mas em 90 ele gera esta mensagem de erro:

Mensagem 207, nível 16, estado 1, linha 3
Nome da coluna inválido 'myname'.

Isso também é explicado na documentação :

Ao vincular as referências da coluna na ORDER BYlista às colunas definidas na SELECTlista, as ambigüidades da coluna são ignoradas e os prefixos das colunas às vezes são ignorados. Isso pode fazer com que o conjunto de resultados retorne em uma ordem inesperada.

Por exemplo, uma ORDER BYcláusula com uma única coluna de duas partes ( <table_alias>.<column>) usada como referência a uma coluna em uma lista SELECT é aceita, mas o alias da tabela é ignorado. Considere a seguinte consulta.

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

Quando executado, o prefixo da coluna é ignorado no ORDER BY. A operação de classificação não ocorre na coluna de origem especificada ( x.c1) conforme o esperado; em vez disso, ocorre no derivadoc1coluna definida na consulta. O plano de execução para esta consulta mostra que os valores da coluna derivada são calculados primeiro e, em seguida, os valores calculados são classificados.


ORDER BY algo que não está na lista SELECT

No modo de compatibilidade 90, você não pode fazer isso:

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:

Msg 104, Nível 16, Estado 1 Os
itens ORDER BY devem aparecer na lista de seleção se a instrução contiver um operador UNION, INTERSECT ou EXCEPT.

Em 80, no entanto, você ainda pode usar esta sintaxe.


Junções externas antigas e nojentas

O modo 80 também permite que você use a sintaxe de junção externa antiga e obsoleta ( *=/=*):

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

No SQL Server 2008/2008 R2, se você tiver 90 anos ou mais, receberá esta mensagem detalhada:

Msg 4147, Nível 15, Estado 1
A consulta usa operadores de junção externos não-ANSI (" *=" ou " =*"). Para executar esta consulta sem modificação, defina o nível de compatibilidade do banco de dados atual como 80, usando a opção SET COMPATIBILITY_LEVEL de ALTER DATABASE. É altamente recomendável reescrever a consulta usando operadores de junção externa ANSI (LEFT OUTER JOIN, RIGHT OUTER JOIN). Nas versões futuras do SQL Server, os operadores de associação que não sejam ANSI não terão suporte, mesmo nos modos de compatibilidade com versões anteriores.

No SQL Server 2012, isso não é mais uma sintaxe válida e gera o seguinte:

Msg 102, Nível 15, Estado 1, Linha 3
Sintaxe incorreta próxima a '* ='.

Obviamente, no SQL Server 2012, você não pode mais solucionar esse problema usando o nível de compatibilidade, pois 80 não é mais suportado. Se você atualizar um banco de dados no modo 80 compatível (por atualização no local, desanexação / conexão, backup / restauração, envio de logs, espelhamento etc.), ele será automaticamente atualizado para 90 para você.


Dicas de tabela sem WITH

No modo 80 compat, você pode usar o seguinte e a dica de tabela será observada:

SELECT * FROM dbo.whatever NOLOCK; 

Em mais de 90 anos, isso NOLOCKnão é mais uma dica de tabela, é um alias. Caso contrário, isso funcionaria:

SELECT * FROM dbo.whatever AS w NOLOCK;

Mas isso não acontece:

Msg 1018, nível 15, estado 1
Sintaxe incorreta perto de 'NOLOCK'. Se isso pretender fazer parte de uma dica de tabela, a palavra-chave e os parênteses A WITH serão obrigatórios. Consulte os Manuais Online do SQL Server para obter a sintaxe adequada.

Agora, para provar que o comportamento não é observado no primeiro exemplo, no modo 90 compatível, use o AdventureWorks (certificando-se de que esteja em um nível compatível mais alto) e execute o seguinte:

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 é particularmente problemático porque o comportamento muda sem uma mensagem de erro ou mesmo um erro. E também é algo que o consultor de atualização e outras ferramentas podem nem encontrar, pois, pelo que sabemos, esse é um alias de tabela.


Conversões envolvendo novos tipos de data / hora

Os novos tipos de data / hora introduzidos no SQL Server 2008 (por exemplo, datee datetime2) oferecem suporte a um intervalo muito maior que o original datetimee smalldatetime). Conversões explícitas de valores fora do intervalo suportado falharão, independentemente do nível de compatibilidade, por exemplo:

SELECT CONVERT(SMALLDATETIME, '00010101');

Rendimentos:

Msg 242, Nível 16, Estado 3
A conversão de um tipo de dados varchar em um tipo de dados smalldatetime resultou em um valor fora do intervalo.

No entanto, as conversões implícitas se resolverão nos níveis de compatibilidade mais recentes. Por exemplo, isso funcionará em mais de 100:

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

Mas em 80 (e também em 90), gera um erro semelhante ao acima:

Msg 242, Nível 16, Estado 3
A conversão de um tipo de dados varchar em um tipo de dados datetime resultou em um valor fora do intervalo.


Cláusulas FOR redundantes nos gatilhos

Este é um cenário obscuro que surgiu aqui . No modo de compatibilidade 80, isso será bem-sucedido:

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

Na compatibilidade 90 e superior, isso não é mais analisado e, em vez disso, você recebe a seguinte mensagem de erro:

Msg 1034, nível 15, estado 1, procedimento tx
Erro de sintaxe: especificação duplicada da ação "UPDATE" na declaração do acionador.


PIVOT / UNPIVOT

Algumas formas de sintaxe não funcionam abaixo dos 80 (mas funcionam bem com mais de 90 anos):

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

Isso produz:

Msg 156, Nível 15, Estado 1
Sintaxe incorreta próxima à palavra-chave 'for'.

Para algumas soluções alternativas, incluindo CROSS APPLY, consulte estas respostas .


Novas funções incorporadas

Tente usar novas funções, como TRY_CONVERT()em um banco de dados com nível de compatibilidade <110. Elas simplesmente não são reconhecidas lá.

SELECT TRY_CONVERT(INT, 1);

Resultado:

Msg 195, nível 15, estado 10
'TRY_CONVERT' não é um nome de função interno reconhecido.


Recomendação

Use o modo de compatibilidade 80 apenas se você realmente precisar. Como ele não estará mais disponível na próxima versão após 2008 R2, a última coisa que você deseja fazer é escrever código nesse nível de compatibilidade, confiar nos comportamentos que você vê e, em seguida, ter um monte de falhas quando você não puder mais use esse nível de compatibilidade. Seja inovador e não tente se esquivar, ganhando tempo para continuar usando a sintaxe antiga e obsoleta.

Aaron Bertrand
fonte
1
Claramente, essa é uma resposta melhor que a minha!
Max Vernon
Muito obrigado por esta resposta elaborada, Aaron! E por corrigir meus inúmeros erros ortográficos.
souplex
1
SQL Server notas 2014 de compatibilidade são encontrados aqui: msdn.microsoft.com/en-us/library/bb510680(v=sql.120).aspx
Josh Gallagher
9

Os níveis de compatibilidade estão presentes apenas para permitir uma migração controlada de uma versão anterior do SQL Server. O Nível de compatibilidade 90 não impede o uso de novos recursos, simplesmente significa que certos aspectos do banco de dados são mantidos de maneira compatível com o funcionamento do SQL Server 2005.

Consulte http://msdn.microsoft.com/en-us/library/bb510680.aspx para obter mais informações.

Max Vernon
fonte