Каково реальное поведение уровня совместимости 80?

47

Может ли кто-нибудь дать мне лучшее представление о функции режима совместимости? Он ведет себя иначе, чем я ожидал.

Насколько я понимаю режимы совместимости, речь идет о доступности и поддержке определенных языковых структур между различными версиями SQL Server.

Это не влияет на внутреннюю работу версии ядра СУБД. Он будет пытаться предотвратить использование функций и конструкций, которые еще не были доступны в более ранних версиях.

Я только что создал новую базу данных с уровнем соответствия 80 в SQL Server 2008 R2. Создал таблицу с одним столбцом int и заполнил ее несколькими строками.

Затем выполнил оператор выбора с row_number()функцией.

Я думал, что, поскольку функция row_number была введена только в 2005 году, это привело бы к ошибке в режиме compat 80.

Но, к моему удивлению, это сработало нормально. Тогда, конечно, правила compat оцениваются только после того, как вы «что-то сохранили». Поэтому я создал сохраненный процесс для моего оператора row_number.

Создание хранимой процедуры прошло успешно, и я могу отлично выполнить ее и получить результаты.

Может ли кто-нибудь помочь мне лучше понять работу режима совместимости? Мое понимание явно ошибочно.

souplex
источник

Ответы:

66

Из документов :

Устанавливает определенные поведения базы данных для совместимости с указанной версией 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. Будьте дальновидны и не пытайтесь загнать себя в угол, выиграв время, чтобы продолжить использовать старый, устаревший синтаксис.

Аарон Бертран
источник
1
Очевидно, что это лучший ответ, чем мой!
Макс Вернон
Большое спасибо за этот сложный ответ, Аарон! И для исправления моих многочисленных орфографических ошибок.
soulex
1
Замечания по
Джош Галлахер,
9

Уровни совместимости присутствуют только для обеспечения контролируемого перехода с более ранней версии SQL Server. Compat Level 90 не исключает использование новых функций, это просто означает, что некоторые аспекты базы данных сохраняются таким образом, чтобы это было совместимо с работой SQL Server 2005.

См. Http://msdn.microsoft.com/en-us/library/bb510680.aspx для получения дополнительной информации.

Макс Вернон
источник