互換性レベル80の実際の動作は何ですか?


47

互換モード機能に関するより良い洞察を誰かが提供してくれますか?予想とは異なる振る舞いをしています。

互換モードを理解している限り、SQL Serverのさまざまなバージョン間の特定の言語構造の可用性とサポートに関するものです。

データベースエンジンバージョンの内部動作には影響しません。以前のバージョンではまだ利用できなかった機能と構成の使用を防止しようとします。

SQL Server 2008 R2で互換レベル80の新しいデータベースを作成しました。単一のint列を持つテーブルを作成し、いくつかの行を追加しました。

次に、row_number()関数を使用してselectステートメントを実行しました。

私の考えでは、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を使用してそれをテーブル値関数に渡そうとすると、構文エラーが発生する可能性があります。これは、最新の互換レベルで機能します。

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に変更し、最後の3行を再度実行します。次のエラーメッセージが表示されます。

メッセージ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
'。'付近の構文が正しくありません。


列名と一致するエイリアスによるORDER BY

次のクエリを検討してください。

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;

結果はmyname80 の式で並べ替えられます。これも、テーブルプレフィックスが無視されるためですが、90ではこのエラーメッセージが生成されます。

メッセージ207、レベル16、状態1、行3
無効な列名 'myname'。

これはすべてドキュメントでも説明されています

ORDER BYリスト内の列参照をリストで定義された列にバインドするとSELECT、列のあいまいさが無視され、列の接頭辞が無視される場合があります。これにより、結果セットが予期しない順序で返される可能性があります。

たとえば、SELECTリスト内の列への参照として使用されるORDER BY2つの部分からなる単一の列(<table_alias>.<column>)を持つ句は受け入れられますが、テーブルエイリアスは無視されます。次のクエリを検討してください。

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

実行されると、列接頭辞は無視されますORDER BY。ソート操作は、指定されたソース列(x.c1)で期待どおりに発生しません。代わりに、派生で発生しますc1クエリで定義されている列。このクエリの実行プランは、派生列の値が最初に計算され、次に計算された値がソートされることを示しています。


SELECTリストにないものによるORDER BY

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外部結合演算子( " *="または " =*")を使用します。変更せずにこのクエリを実行するには、ALTER DATABASEのSET COMPATIBILITY_LEVELオプションを使用して、現在のデータベースの互換性レベルを80に設定してください。ANSI外部結合演算子(LEFT OUTER JOIN、RIGHT OUTER JOIN)を使用してクエリを書き換えることを強くお勧めします。SQL Serverの将来のバージョンでは、非ANSI結合演算子は下位互換性モードでもサポートされなくなります。

SQL Server 2012では、これはもはや有効な構文ではなく、次のようになります。

メッセージ102、レベル15、状態1、行3
'* ='付近の構文が正しくありません 。

もちろん、SQL Server 2012では、80がサポートされなくなったため、互換性レベルを使用してこの問題を回避することはできなくなりました。80互換モードでデータベースをアップグレードすると(インプレースアップグレード、デタッチ/アタッチ、バックアップ/復元、ログ配布、ミラーリングなどにより)、自動的に90にアップグレードされます。


WITHなしのテーブルヒント

80互換モードでは、次を使用できます。テーブルヒントが観察されます。

SELECT * FROM dbo.whatever NOLOCK; 

90+では、これNOLOCKはもはやテーブルヒントではなく、エイリアスです。そうでなければ、これは動作します:

SELECT * FROM dbo.whatever AS w NOLOCK;

しかし、そうではありません:

メッセージ1018、レベル15、状態1
'NOLOCK'付近の構文が正しくありません。これがテーブルヒントの一部として意図されている場合、WITHキーワードと括弧が必要になりました。適切な構文については、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');

利回り:

メッセージ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
構文エラー:トリガー宣言内のアクション「UPDATE」の指定が重複しています。


ピボット/アンピボット

一部の形式の構文は80未満では機能しません(ただし、90以上では正常に機能します)。

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

これにより以下が得られます。

メッセージ156、レベル15、状態1
キーワード「for」付近の構文が正しくありません。

などの回避策CROSS APPLYについては、これらの回答を参照してください。


新しい組み込み関数

TRY_CONVERT()互換性レベルが110未満のデータベースのような新しい関数を使用してみてください。それらはそこではまったく認識されません。

SELECT TRY_CONVERT(INT, 1);

結果:

メッセージ195、レベル15、状態10
'TRY_CONVERT'は認識される組み込み関数名ではありません。


勧告

実際に必要な場合にのみ80互換モードを使用してください2008 R2以降の次のバージョンでは使用できなくなるため、最後に行う必要があるのは、この互換性レベルでコードを記述し、表示されている動作に依存し、それ以上できなくなったときに大量の破損が発生することですその互換レベルを使用します。前向きに考え、時間をかけて古い廃止された構文を使い続けることで自分を隅に追いやろうとしないでください。


1
明らかにそれは私のものよりも良い答えです!
マックスヴァーノン

この手の込んだ答え、アーロンに感謝します!そして、私の多くのスペルミスを修正してくれました。
souplex

1
SQL Server 2014の互換性に関する注意事項は次のとおりです。msdn.microsoft.com
ジョシュギャラガー14年

9

互換性レベルは、SQL Serverの以前のバージョンからの制御された移行を可能にするためにのみ存在します。Compatレベル90は、新機能の使用を妨げるものではなく、単にSQL Server 2005の動作と互換性のある方法でデータベースの特定の側面が保持されることを意味します。

詳細については、http://msdn.microsoft.com/en-us/library/bb510680.aspx参照してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.