2015-12-16 10 views
5

(私はひどい説明については、事前に謝罪、しかし、あなたはあなたの下のクエリを実行する場合、私は何を意味するか見るべきである!)なぜif existsがfalseを返すとしても、SQLはif exists構文の真のセクションで文を評価しますか?

なぜMSSQLでもif exists戻った場合、if existsコンストラクトのtrueセクションでステートメントを評価しません偽、エラーを引き起こす?

たとえば、以下の2つのクエリでは、最初にテーブルが存在するかどうかをチェックし、テーブルに特定の列があるかどうかを確認します。何らかの理由で、テーブルが存在するが列は存在しないため、このクエリを実行すると次のエラーがスローされます。

Msg 207, Level 16, State 1, Line 21 
Invalid column name 'colB'. 
Msg 207, Level 16, State 1, Line 21 
Invalid column name 'colC'. 
Msg 207, Level 16, State 1, Line 21 
Invalid column name 'colA'. 

私はエラーをスローせずに、構造物のfalsepart上に移動するにはSQLのためだった、ここで期待される動作。 (次のクエリと同じように)

ただし、2番目のスクリプト(同じバーテーブル名)は正常に実行されます。これは、クエリが検索しているテーブルが存在しないためです。

--Scripts to setup the example. 
CREATE DATABASE TEST 
GO 
USE TEST 
GO 
CREATE TABLE t1 (colD VARCHAR(255)) --Create a table with the correct name, but incorrect column names. 
GO 

--This query fails, because t1 exists, even though the columns in t1 don't. 
IF EXISTS (select * from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1' AND COLUMN_NAME IN ('colA','colB','colC')) 
BEGIN 
    SELECT colA FROM t1 WHERE colB = 0 AND colC = 1 
END 
ELSE BEGIN 
    SELECT 'FALSE' 
END 

GO 

--This query executes ok, because t2 does not exist. 
IF EXISTS (select * from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't2' AND COLUMN_NAME IN ('colA','colB','colC')) 
BEGIN 
    SELECT colA FROM t2 WHERE colB = 0 AND colC = 1 
END 
ELSE BEGIN 
    SELECT 'FALSE' 
END 

誰も私に説明することができますなぜ最初のクエリのエラーは、2番目のクエリが正常に実行されますか?

はこれまでのところ、私は唯一のMicrosoft SQL Server 2012

+4

バッチをコンパイルするのに失敗しました。コードを実行しているところにさえ達していません。特定のクラスに特定のメソッドが含まれているかどうかを確認するために、リフレクション呼び出しの負荷がある場合は、C#で検討してください。それらのチェックを 'if'で持つことはできませんし、' if'の本体の中でクラスのインスタンス上でそのメソッドを呼び出す*という行を持つことはできません - 同じ理由で*失敗する*コンパイル*。 –

+0

@Damien_The_Unbeliever、私はあなたが意味することを理解していますが、オブジェクトのいくつか*が存在しないためにバッチをコンパイルできない場合、オブジェクトの* none *が存在すると、 – KidCode

+4

テーブルの状況はわずかですが、[遅延名解決](https://technet.microsoft.com/en-us/library/ms190686(v = sql.105).aspx)(1か所文が到達するまでSQLが意図的にコンパイルを延期します) –

答えて

4

この質問の最初の部分に答えるには何らかの形式の実行時型検査(Reflectionなど)を持つ言語(C#など)に精通していると仮定します。

SomeType t = GetSomeTypeFromSomewhere(); 
if(t.GetType().GetMethod("FunTimes")!=null) 
{ 
    t.FunTimes(); 
} 

そしてSomeTypeFunTimesというパブリックメソッドが含まれていないことを前提としています

は、あなたがこのようなコードがあるとします。 FunTimesメソッドを呼び出しようとしたときに警備員を書いたとしても、エラーが発生します。そして、具体的にはコンパイル時エラーです - C#コンパイラはコードを生成することさえできません。コードを実行し、結果をGetMethod()から取得し、ネストされたブロック内でコードを実行しないことを決定します。

があなたのコードに戻すには、分析の正確な同じタイプは、ここで適用されます。

IF EXISTS (select * from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1' AND COLUMN_NAME IN ('colA','colB','colC')) 
BEGIN 
    SELECT colA FROM t1 WHERE colB = 0 AND colC = 1 
END 
ELSE BEGIN 
    SELECT 'FALSE' 
END 

SQL Serverがこのバッチをコンパイルしようとすると失敗します。コードを実行することは決してないので、どのブランチ(IFまたはELSE)を取るべきかを決める時点まで決して到達しません。

上記のすべてが当てはまる場合、なぜ2番目のコードが機能するのですか?これは、Deferred Name ResolutionというT-SQLの特定の機能が原因です。基本的には、欠落しているオブジェクトがテーブル(またはオブジェクトが見つかるまで区別できないため、ビュー)の場合に適用される特別なルールがあります。その特定のインスタンスでは、SQL Serverはすぐにコンパイルエラーを通知しません。

遅延名解決では、実行が開始され、スキーマの変更(不足しているテーブル/ビューの追加など)によってシステムがコードの残りの部分を再コンパイルします。

0

でこれをテストするために管理してきた私は、あなたが間違った結果を評価していると思う(そして、それはあなたのせいでは私見ではありません)。

EXISTS部分は、どちらの場合もFALSEを返します。ただし、SQLクエリパーサーは面白いですが、内部式を解析してステートメントの実行前にエラーを表示します。列がない場合にのみエラーが発生し、テーブルがない場合はエラーになりません。

TRUEに評価されている最初のクエリでは、テーブル名をt2のように変更してみてください。実行され、両方でFALSEと評価されます。

関連する問題