9

これが適切でない場合はお詫びしますが、実際にはこれは「方法」ではなく「理由」です。それが適切かどうかはわかりませんが、質問するのに適した場所がわからず、探しているものを取得するためにグーグルを表現する方法を考えることができません。

    IF 'hell' = 'freezing over'
    BEGIN
    SELECT log(0)
    END

その声明を見てください。IF句が真になる世界はありません。それを実行しようとすると、SQLがIF句を飛び越えて、最後に移動することを期待しています。代わりに私は得る:

An invalid floating point operation occurred.

これは奇妙です。ですから、それがSQLのやり方だと思います。それ外...

    IF 'hell' = 'freezing over'
    BEGIN
    SELECT 1/0
    END

ここではエラーはありません。IF句のステートメントは、引き続きエラーを生成するはずです。なぜこれが起こらないのか誰かが説明できますか?

これは、EXP(SUM(LOG()))を使用してif句内にデータを蓄積するSQL計算の大規模なセットをデバッグしているときに発生しました。コードを変更して、それが再び発生しないようにすることはできますが、IF句内で満たされていないものを評価するのはなぜですか。

乾杯。

編集:追加の娯楽。キャッチしてみませんか?Pffft

    IF 1=2
    BEGIN
        BEGIN TRY
            SELECT SQRT(-1)
        END TRY
        BEGIN CATCH
        END CATCH
    END

非数学:

    IF 1=2
    BEGIN
    SELECT SUBSTRING('hello',-1,-1)
    END
4

4 に答える 4

7

私の推測では、定数畳み込みのlog(0)ために効果的に評価されるのは時期尚早ですが、カーディナリティの推定、またはANSI_WARNINGS設定がゼロ除算の望ましい結果(オーバーフローとNULL)に影響を与える可能性が高いため、そうではありません。1/0

于 2012-05-22T14:29:55.393 に答える
2

パーサーには、IFロジックに従うための賢さがありません。次の例を考えてみましょう。

IF (1 = 0)
BEGIN
   CREATE TABLE #t(x INT):
END
ELSE
BEGIN 
   CREATE TABLE #t(x INT):
END

実行する場合でも、解析する場合でも、パーサーはCREATE TABLEバッチ内のすべてのステートメントを調べて、テーブルを2回作成しようとしたと判断します(これを行うには、最初のコピーが存在する必要はありません)。結果:

メッセージ2714、レベル16、状態1、行7
データベースにはすでに「#t」という名前のオブジェクトがあります。

パーサーがあなたほど賢くないよりも、私があなたに良い答えを持っているかどうかは本当にわかりません。

動的SQLを使用して実行時まで問題を延期することにより、パーサーを無効にすることができます。

IF 'hell' = 'freezing over'
BEGIN
  EXEC sp_executesql N'SELECT log(0);';
END

しかし、私は疑問に思う必要があります。決して真になることのない条件のために足場を設定し、エラーが発生することがわかっているステートメントを発行することのポイントは何ですか?

于 2012-05-22T14:18:21.717 に答える
2

それを実行しようとすると、SQLがIF句を飛び越えて、最後に移動することを期待しています。

バッチを実行すると、3つのことが起こります

  1. SQLが解析されます

  2. SQLがコンパイルされます

  3. SQLが実行されます

残念なことに、SQL Serverのバッチでコンパイルエラーと実行エラーの両方が発生すると、同じ「クエリがエラーで完了しました」というメッセージが表示されます。それでは、違いがわかりやすい手順を使用しましょう

次のことを考慮してください

Create proc compiletime
as 
 SELECT log(0)
 SELECT SQRT(-1)
 SELECT SUBSTRING('hello',-1,-1)
 SELECT 1/0

その手順は正常に解析されます。ただし、パラメータとして無効な定数がいくつかあるため、最初の3つのSELECTを削除しない限りコンパイルできません。実行時エラーではなくコンパイル時エラーも発生した場合は便利ですSELECT 1/0が、@ Alex Kが指摘しているように、動作はANSI_WARNINGSに基づいているため、コンパイル時エラーではありません。

そのため、最初の2つとの間に違いが見られます。また、コンパイル時エラーのためにTRYCATCHが機能しなかった理由についても説明します。

では、なぜSQLサーバーが到達不能コードをコンパイルするのでしょうか。一般に、到達不能であることを知るためには、停止性問題の解決策が必要だからです。場合によっては解決できますが、これは...


DECLARE @x as integer
SET @x = SomeFunction()
If (1 = @x)
   SomeCompiletime error

動作が異なるため、さらに混乱します。

if (1=0)
   SomeCompiletime error
于 2012-05-22T15:19:27.433 に答える
0

明らかに、SQLコンパイラがコンパイル時と実行時に評価しようとしている式がいくつかあります。私の推測では、分割はそれほど高価ではないと考えられているため、実行時に延期されます。一方、log()のような非常に複雑なものは高価であり、コンパイル時に実行したいと考えています。

これは推測です。また、そのような違いは非決定論的であることも意味します。特定のスクリプトで機能するものと機能しないものを特定する必要があります。データベースのリリース間で動作が変わる可能性があります。

于 2012-05-22T14:24:23.767 に答える