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


47

Чи міг би хтось надати мені кращу інформацію про функцію сумісного режиму? Він поводиться інакше, ніж я очікував.

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

Це не впливає на внутрішню роботу версії двигуна бази даних. Це намагатиметься запобігти використанню функцій та конструкцій, які ще не були доступні в попередніх версіях.

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

Потім виконується оператор select з row_number()функцією.

Думаю, оскільки функція row_number була введена лише в 2005 році, це призведе до помилки в режимі compat 80.

Але на мій подив, це спрацювало чудово. Тоді, безумовно, правила супроводу оцінюються лише після того, як ви щось збережете. Тому я створив збережений додаток для мого оператора row_number.

Збережене створення протоколу пройшло чудово, і я можу його ідеально виконати та отримати результати.

Може хтось допоможе мені краще зрозуміти роботу режиму сумісності? Моє розуміння, очевидно, недоліки.

Відповіді:


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 та спроби передати її функції, що оцінюється за таблицею. Це працює на сучасних рівнях 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);

Однак змініть рівень сумісності на 80 та запустіть останні три рядки знову; ви отримуєте це повідомлення про помилку:

Повідомлення 137, рівень 16, стан 1, рядок 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 кваліфікований стовпець, переданий функції, викликає загальну помилку синтаксису:

Повідомлення 102, рівень 15, стан 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пропозиція з одним двочастинним стовпцем ( <table_alias>.<column>), який використовується як посилання на стовпець у списку SELECT, приймається, але псевдонім таблиці ігнорується. Розглянемо наступний запит.

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

При виконанні префікс стовпця ігнорується в ORDER BY. Операція сортування не відбувається у вказаному стовпці джерела ( x.c1), як очікувалося; натомість це відбувається на похідномуc1стовпчик, який визначений у запиті. План виконання цього запиту показує, що значення для похідного стовпця спочатку обчислюються, а потім обчислюються значення.


ЗАМОВИТИ Щось, що не в списку SELECT

У режимі сумісності 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
ЗАМОВЛЕННЯ ПО ЕЛЕМЕНТУ повинні з'являтися у списку вибору, якщо заява містить оператора 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 років або більше, ви отримуєте це багатослівне повідомлення:

Msg 4147, рівень 15, стан 1
У запиті використовуються оператори зовнішнього з'єднання, які не є ANSI (" *=" або " =*"). Для запуску цього запиту без змін, будь ласка, встановіть рівень сумісності для поточної бази даних на 80, використовуючи опцію SET COMPATIBILITY_LEVEL ALTER DATABASE. Настійно рекомендується перезаписувати запит, використовуючи зовнішні оператори ANSI (приєднайтесь ліворуч, ПРИЄДНО ПРИЄДНАЙТЕСЬ). У майбутніх версіях SQL Server оператори приєднання, що не належать до ANSI, не підтримуватимуться навіть у режимах сумісності із зворотним.

У SQL Server 2012 цей синтаксис більше не є дійсним і отримує наступне:

Повідомлення 102, рівень 15, стан 1, рядок 3
Неправильний синтаксис поблизу '* ='.

Звичайно, у SQL Server 2012 ви більше не можете вирішити цю проблему, використовуючи рівень сумісності, оскільки 80 більше не підтримується. Якщо ви оновите базу даних у режимі 80 компат (за допомогою оновлення на місці, відключення / приєднання, резервного копіювання / відновлення, доставки журналу, дзеркального відображення тощо), вона автоматично буде оновлена ​​до 90 для вас.


Підказки таблиці без З

У режимі 80 компат можна використовувати наступне, і натяк на таблицю буде дотримано:

SELECT * FROM dbo.whatever NOLOCK; 

У 90+ NOLOCKце вже не натяк на таблицю, це псевдонім. Інакше це спрацює:

SELECT * FROM dbo.whatever AS w NOLOCK;

Але це не так:

Повідомлення 1018, рівень 15, стан 1
Неправильний синтаксис біля "NOLOCK". Якщо це призначено як частина натяку на таблицю, тепер потрібні ключове слово AND дужки. Дивіться SQL Server Books Online для отримання відповідного синтаксису.

Тепер, щоб довести, що поведінка не спостерігається в першому прикладі, коли в режимі 90 компат, використовуйте AdventureWorks (переконайтесь, що вона знаходиться на більш високому рівні компат) і виконайте наступне:

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

Врожайність:

Msg 242, Рівень 16, Стан 3
Перетворення типу даних varchar у тип даних для малого часу приводило до значення поза діапазоном.

Однак неявні перетворення працюватимуть самі на нових рівнях сумісності. Наприклад, це буде працювати в 100+:

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

Але в 80 (і також в 90-х) він створює аналогічну помилку, як і вище:

Msg 242, Рівень 16, Стан 3
Перетворення типу даних varchar у тип даних дати дату призвело до значення поза діапазоном.


Надлишкові ЗА застереження в тригерах

Це незрозумілий сценарій, який з’явився тут . У режимі сумісності 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 і вище це більше не аналізується, і натомість ви отримуєте таке повідомлення про помилку:

Msg 1034, рівень 15, стан 1, процедура tx
Помилка синтаксису: Дублювання специфікації дії "UPDATE" в декларації тригера.


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 лише тоді, коли він вам справді потрібен. Оскільки він не буде доступний у наступній версії після R2 2008 року, останнє, що ви хочете зробити, - це написати код на цьому рівні співзвучності, покластися на поведінку, яку ви бачите, а потім мати цілу купу поломки, коли вже не зможете. використовувати цей рівень співзвучності. Будьте думкою вперед і не намагайтеся малювати себе в кутку, купуючи час, щоб продовжити використання старого, застарілого синтаксису.


1
Очевидно, що це краща відповідь, ніж моя!
Макс Вернон

Дуже дякую за цю ретельну відповідь, Аароне! І для виправлення моїх численних орфографічних помилок.
souplex

1
Примітки про сумісність SQL Server 2014 можна знайти тут: msdn.microsoft.com/en-us/library/bb510680(v=sql.120).aspx
Джош Галлахер

9

Рівні сумісності існують лише для того, щоб дозволити контрольовану міграцію з попередньої версії SQL Server. Compat Level 90 не виключає використання нових функцій, це просто означає, що певні аспекти бази даних зберігаються таким чином, що сумісний з тим, як працював SQL Server 2005.

Докладнішу інформацію див. У розділі http://msdn.microsoft.com/en-us/library/bb510680.aspx .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.