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 SELECT
lista. 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 BY
alias 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 myname
expressã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 BY
lista às colunas definidas na SELECT
lista, 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 BY
clá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 derivadoc1
coluna 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 NOLOCK
nã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, date
e datetime2
) oferecem suporte a um intervalo muito maior que o original datetime
e 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.