Из документов :
Устанавливает определенные поведения базы данных для совместимости с указанной версией SQL Server.
...
Уровень совместимости обеспечивает только частичную обратную совместимость с более ранними версиями SQL Server. Используйте уровень совместимости в качестве промежуточного средства миграции для обхода различий версий в поведении, которое контролируется соответствующей настройкой уровня совместимости.
В моей интерпретации режим совместимости связан с поведением и синтаксическим анализом, а не с такими вещами, как синтаксический анализатор, говорящий: «Эй, ты не можешь использовать ROW_NUMBER()
!» Иногда более низкий уровень совместимости позволяет вам продолжать использовать синтаксис, который больше не поддерживается, а иногда он не позволяет использовать новые синтаксические конструкции. В документации перечислено несколько явных примеров, но вот несколько демонстраций:
Передача встроенных функций в качестве аргументов функции
Этот код работает на уровне совместимости 90+:
SELECT *
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, NULL);
Но в 80 это дает:
Сообщение 102, уровень 15, состояние 1
Неверный синтаксис рядом с '('.
Конкретная проблема здесь в том, что в 80 вы не можете передавать встроенную функцию в функцию. Если вы хотите остаться в режиме совместимости 80, вы можете обойти это, сказав:
DECLARE @db_id INT = DB_ID();
SELECT *
FROM sys.dm_db_index_physical_stats(@db_id, NULL, NULL, NULL, NULL);
Передача табличного типа в табличную функцию
Как и выше, вы можете получить синтаксическую ошибку при использовании TVP и попытке передать его в табличную функцию. Это работает на современных уровнях:
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);
Однако измените уровень совместимости на 80 и повторите последние три строки; вы получаете это сообщение об ошибке:
Msg 137, Level 16, State 1, Line 19
Должен объявить скалярную переменную "@foo".
Не очень хороший обходной путь у меня на макушке, кроме повышения уровня компаса или получения результатов другим способом.
Использование квалифицированных имен столбцов в APPLY
В режиме совместимости 90 и выше вы можете сделать это без проблем:
SELECT * FROM sys.dm_exec_cached_plans AS p
CROSS APPLY sys.dm_exec_sql_text(p.plan_handle) AS t;
Однако в режиме совместимости 80 квалифицированный столбец, передаваемый функции, вызывает общую синтаксическую ошибку:
Msg 102, Level 15, State 1
Неверный синтаксис рядом с '.'.
ЗАКАЗАТЬ по псевдониму, который совпадает с именем столбца
Рассмотрим этот запрос:
SELECT name = REVERSE(name), realname = name
FROM sys.all_objects AS o
ORDER BY o.name;
В режиме совместимости 80 результаты следующие:
001_ofni_epytatad_ps sp_datatype_info_100
001_scitsitats_ps sp_statistics_100
001_snmuloc_corps_ps sp_sproc_columns_100
...
В режиме совместимости 90 результаты совсем другие:
snmuloc_lla all_columns
stcejbo_lla all_objects
sretemarap_lla all_parameters
...
Причина? В режиме совместимости 80 префикс таблицы полностью игнорируется, поэтому он упорядочивается по выражению, определенному псевдонимом в SELECT
списке. На более новых уровнях совместимости учитывается префикс таблицы, поэтому SQL Server будет фактически использовать этот столбец в таблице (если он найден). Если ORDER BY
псевдоним не найден в таблице, более новые уровни совместимости не так просты для неоднозначности. Рассмотрим этот пример:
SELECT myname = REVERSE(name), realname = name
FROM sys.all_objects AS o
ORDER BY o.myname;
Результат упорядочен myname
выражением в 80, потому что снова префикс таблицы игнорируется, но в 90 он генерирует это сообщение об ошибке:
Сообщение 207, Уровень 16, Состояние 1, Строка 3
Неверное имя столбца «Мое имя».
Это все также объясняется в документации :
При привязке ссылок на столбцы в ORDER BY
списке к столбцам, определенным в SELECT
списке, неоднозначности столбцов игнорируются, а префиксы столбцов иногда игнорируются. Это может привести к тому, что набор результатов вернется в неожиданном порядке.
Например, ORDER BY
предложение с единственным состоящим из двух частей column ( <table_alias>.<column>
), которое используется в качестве ссылки на столбец в списке SELECT, принимается, но псевдоним таблицы игнорируется. Рассмотрим следующий запрос.
SELECT c1 = -c1 FROM t_table AS x ORDER BY x.c1
При выполнении префикс столбца игнорируется в ORDER BY
. Операция сортировки не выполняется в указанном столбце источника ( x.c1
), как ожидалось; вместо этого это происходит на основе производногоc1
столбец, определенный в запросе. План выполнения для этого запроса показывает, что сначала вычисляются значения для производного столбца, а затем сортируются вычисленные значения.
ЗАКАЗАТЬ что-то, чего нет в списке ВЫБРАТЬ
В режиме совместимости 90 вы не можете сделать это:
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;
Результат:
Сообщение 104, уровень 16, состояние 1
Элементы ORDER BY должны отображаться в списке выбора, если инструкция содержит оператор UNION, INTERSECT или EXCEPT.
В 80, однако, вы все еще можете использовать этот синтаксис.
Старые, неприглядные внешние соединения
Режим 80 также позволяет использовать старый устаревший синтаксис внешнего соединения ( *=/=*
):
SELECT o.name, c.name
FROM sys.objects AS o, sys.columns AS c
WHERE o.[object_id] *= c.[object_id];
В SQL Server 2008/2008 R2, если вы старше 90 лет, вы получите это подробное сообщение:
Сообщение 4147, уровень 15, состояние 1
В запросе используются не-ANSI операторы внешнего соединения (" *=
" или " =*
"). Чтобы выполнить этот запрос без изменений, установите уровень совместимости для текущей базы данных равным 80, используя параметр SET COMPATIBILITY_LEVEL в ALTER DATABASE. Настоятельно рекомендуется переписать запрос с использованием операторов внешнего соединения ANSI (LEFT OUTER JOIN, RIGHT OUTER JOIN). В будущих версиях SQL Server операторы соединения не-ANSI не будут поддерживаться даже в режимах обратной совместимости.
В SQL Server 2012 этот синтаксис больше не является допустимым и выдает следующее:
Сообщение 102, уровень 15, состояние 1, строка 3
Неправильный синтаксис рядом с '* ='.
Конечно, в SQL Server 2012 вы больше не можете обойти эту проблему, используя уровень совместимости, так как 80 больше не поддерживается. Если вы модернизируете базу данных в режиме 80 compat (путем обновления на месте, отсоединения / присоединения, резервного копирования / восстановления, доставки журналов, зеркалирования и т. Д.), Она будет автоматически обновлена до 90 для вас.
Таблица подсказок без СО
В режиме 80 compat вы можете использовать следующее, и табличная подсказка будет соблюдена:
SELECT * FROM dbo.whatever NOLOCK;
В 90+ NOLOCK
это уже не настольная подсказка, а псевдоним. В противном случае это будет работать:
SELECT * FROM dbo.whatever AS w NOLOCK;
Но это не так:
Сообщение 1018, уровень 15, состояние 1
Неверный синтаксис рядом с NOLOCK. Если это предназначено как часть табличной подсказки, теперь требуются ключевое слово WITH и скобки. См. SQL Server Books Online для правильного синтаксиса.
Теперь, чтобы доказать, что поведение не наблюдается в первом примере, когда в режиме Compat 90, используйте AdventureWorks (убедитесь, что он находится на более высоком уровне Compat) и выполните следующее:
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;
Это особенно проблематично, потому что поведение меняется без сообщения об ошибке или даже ошибки. И это также то, что советник по обновлению и другие инструменты могут даже не заметить, поскольку, насколько он знает, это псевдоним таблицы.
Конверсии с использованием новых типов даты / времени
Новые типы даты / времени, представленные в SQL Server 2008 (например, date
и datetime2
), поддерживают гораздо больший диапазон, чем исходные datetime
и smalldatetime
). Явное преобразование значений вне поддерживаемого диапазона завершится неудачей независимо от уровня совместимости, например:
SELECT CONVERT(SMALLDATETIME, '00010101');
Урожайность:
Сообщение 242, уровень 16, состояние 3
Преобразование типа данных varchar в тип данных smalldatetime привело к значению вне допустимого диапазона.
Однако неявные преобразования будут работать на более новых уровнях совместимости. Например, это будет работать в 100+:
SELECT DATEDIFF(DAY, CONVERT(SMALLDATETIME, SYSDATETIME()), '00010101');
Но в 80 (а также в 90) выдает ошибку, аналогичную приведенной выше:
Сообщение 242, уровень 16, состояние 3
Преобразование типа данных varchar в тип данных datetime привело к значению вне допустимого диапазона.
Избыточные предложения FOR в триггерах
Это неясный сценарий, который возник здесь . В режиме совместимости 80 это будет успешным:
CREATE TABLE dbo.x(y INT);
GO
CREATE TRIGGER tx ON dbo.x
FOR UPDATE, UPDATE
------------^^^^^^ notice the redundant UPDATE
AS PRINT 1;
При совместимости 90 и выше это больше не анализируется, и вместо этого вы получаете следующее сообщение об ошибке:
Сообщение 1034, уровень 15, состояние 1, процедура tx
Синтаксическая ошибка: дублирующая спецификация действия «ОБНОВЛЕНИЕ» в объявлении триггера.
PIVOT / UNPIVOT
Некоторые формы синтаксиса не будут работать под 80 (но отлично работают в 90+):
SELECT col1, col2
FROM dbo.t1
UNPIVOT (value FOR col3 IN ([x],[y])) AS p;
Это дает:
Сообщение 156, уровень 15, состояние 1
Неправильный синтаксис рядом с ключевым словом «для».
Для некоторых обходных путей, в том числе CROSS APPLY
, смотрите эти ответы .
Новые встроенные функции
Попробуйте использовать новые функции, например, TRY_CONVERT()
в базе данных с уровнем совместимости <110. Они там просто не распознаются.
SELECT TRY_CONVERT(INT, 1);
Результат:
Сообщение 195, уровень 15, состояние 10
«TRY_CONVERT» не является распознанным именем встроенной функции.
Рекомендация
Используйте режим совместимости 80, только если он вам действительно нужен. Так как он больше не будет доступен в следующей версии после 2008 R2, последнее, что вы хотите сделать, это написать код на этом уровне компатации, полагаться на поведение, которое вы видите, а затем иметь кучу поломок, когда вы больше не можете использовать этот уровень Compat. Будьте дальновидны и не пытайтесь загнать себя в угол, выиграв время, чтобы продолжить использовать старый, устаревший синтаксис.