WHERE句の参照エイリアス(SELECTで計算)


130
SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue
FROM Invoices
WHERE BalanceDue > 0 --error

選択した列のリストで変数として設定されている計算値「BalanceDue」は、WHERE句では使用できません。

それができる方法はありますか?この関連する質問(Where句でMySQL Select Statmentの変数を使用する)では、答えは実際にはいいえ、計算を2回書き出す(そしてクエリでその計算を実行する)ようであるようです。満足です。

回答:


237

SELECTは評価される最後の2番目の句であるため、ORDER BY以外ではエイリアスを参照できません。2つの回避策:

SELECT BalanceDue FROM (
  SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue
  FROM Invoices
) AS x
WHERE BalanceDue > 0;

または単に式を繰り返します:

SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue
FROM Invoices
WHERE  (InvoiceTotal - PaymentTotal - CreditTotal)  > 0;

私は後者を好みます。式が非常に複雑である(または計算にコストがかかる)場合は、代わりに計算列(およびおそらく永続化)を検討する必要があります(特に、多くのクエリがこの同じ式を参照している場合)。

PSあなたの恐れは根拠がないようです。少なくともこの簡単な例では、SQL Serverは2回参照したとしても、計算を1回だけ実行するのに十分なほどスマートです。先に進んで計画を比較してください。それらは同一であることがわかります。式が複数回評価されるというより複雑なケースがある場合は、より複雑なクエリとプランを投稿してください。

以下は、まったく同じ実行プランを生成する5つのクエリ例です。

SELECT LEN(name) + column_id AS x
FROM sys.all_columns
WHERE LEN(name) + column_id > 30;

SELECT x FROM (
SELECT LEN(name) + column_id AS x
FROM sys.all_columns
) AS x
WHERE x > 30;

SELECT LEN(name) + column_id AS x
FROM sys.all_columns
WHERE column_id + LEN(name) > 30;

SELECT name, column_id, x FROM (
SELECT name, column_id, LEN(name) + column_id AS x
FROM sys.all_columns
) AS x
WHERE x > 30;

SELECT name, column_id, x FROM (
SELECT name, column_id, LEN(name) + column_id AS x
FROM sys.all_columns
) AS x
WHERE LEN(name) + column_id > 30;

5つのクエリすべての結果の計画:

ここに画像の説明を入力してください


11
ワオ。SQL Serverは、一度だけ計算を実行するためのスマート十分である
alternatefaraz

5
これは非常に質の高い回答です。
シッダールタ

MERGEステートメントで追加の条件文が必要でしたが、これが私がそれを機能させる唯一の方法でした。ありがとう!
Eric Burdo

1
@EricBurdoを使用しMERGEている場合は、これらすべてを考慮に入れていることを確認MERGEしてください
アーロンバートランド

11

あなたはこれを使うことができます cross apply

SELECT c.BalanceDue AS BalanceDue
FROM Invoices
cross apply (select (InvoiceTotal - PaymentTotal - CreditTotal) as BalanceDue) as c
WHERE  c.BalanceDue  > 0;

4

SELECT、WHERE、その他の句の両方で使用できる変数を効果的に定義することは実際に可能です。

クロス結合では、参照されるテーブル列への適切なバインディングが必ずしも可能とは限りませんが、OUTER APPLYでは可能であり、nullをより透過的に扱います。

SELECT
    vars.BalanceDue
FROM
    Entity e
OUTER APPLY (
    SELECT
        -- variables   
        BalanceDue = e.EntityTypeId,
        Variable2 = ...some..long..complex..expression..etc...
    ) vars
WHERE
    vars.BalanceDue > 0

Syed Mehroz Alamへの称賛

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