SELECT句とGROUP BY句の両方のSQL計算フィールド


11

多くの場合、MS SQL Serverデータベースのクエリで、次のような計算フィールドを作成する必要があります

(CASE WHEN A.type = 'Workover' THEN 'Workover' 
      ELSE (CASE WHEN substring(C.category, 2, 1) = 'D' THEN 'Drilling' 
                 WHEN substring(C.category, 2, 1) = 'C' THEN 'Completion' 
                 WHEN substring(C.category, 2, 1) = 'W' THEN 'Workover' 
                 ELSE 'Other' 
            END)
END)

次に、この計算フィールド(特に)で結果をグループ化する必要があります。したがって、SELECT句とGROUP BY句の両方で同じ計算を行っています。SQLサーバーは実際にこれらの計算を2回実行していますか、それとも1回だけ実行するのに十分スマートですか?

回答:


13

SELECT句とGROUP BY句の両方で同じ計算を行っています。SQLサーバーは実際にこれらの計算を2回実行していますか、それとも1回だけ実行するのに十分スマートですか?

簡単な答えは、SQL Serverはスカラー式が実行時にいつ、何回評価されるかについての一般的な保証をしないということです。

スカラー式の配置、実行、およびキャッシュに関して、オプティマイザと実行エンジン内には、あらゆる種類の複雑な(そして文書化されていない)動作があります。Books Onlineはこれについて言うことは多くありませんが、それが言うことはこれです:

スカラー記述の計算

これは、私が以前に言及した動作の1つである、式の実行の遅延について説明しています。他の現在の動作(いつでも変更される可能性があります)については、このブログの投稿で書きました

もう1つの考慮事項は、クエリオプティマイザーで使用されるコストモデルは、現在スカラー式のコスト見積もりの​​方法ではあまり機能しないことです。堅牢な原価計算フレームワークがなければ、現在の結果は幅広いヒューリスティックスまたは純粋なチャンスに基づいています。

非常に単純な式の場合、ほとんどの場合、式が1回評価されるか、何度評価されるかは、ほとんど違いがありません。とは言っても、式が冗長に何度も評価されるとパフォーマンスに悪影響が及ぶ、または実行の並列ブランチで評価すると有利である単一スレッドで評価が発生する、大規模なクエリに遭遇しました予定。

要約すると、現在の動作は未定義であり、何が起こったかを理解するのに役立つ実行計画には何もありません(そして、ブログの投稿のように、詳細なエンジンの動作を調べるためにデバッガーを接続することは必ずしも便利ではありません)。

スカラー評価の問題がパフォーマンスに影響する場合は、Microsoftサポートに問題を報告してください。これは、製品の将来のバージョンを改善するためのフィードバックを提供する最良の方法です。


3

あなたの質問へのコメントが述べているように、答えは(私の経験では、少なくとも)「はい」です。SQL Serverは通常、再計算を回避するのに十分なほどスマートです。SQL Server Management Studio内から実行プランを表示することで、おそらくこれを確認できます。各計算フィールドが指定されていますExprxxxxx(xxxxxは数値です)。検索対象がわかっている場合は、同じ式を使用していることを確認できるはずです。

ディスカッションに追加するために、他の美的オプションは一般的なテーブル式です:

with [cte] as
(
    select
        (case when a.type = 'workover' then 'workover' else 
        (case when substring(c.category, 2, 1) = 'd' then 'drilling'
              when substring(c.category, 2, 1) = 'c' then 'completion'
              when substring(c.category, 2, 1) = 'w' then 'workover'
              else 'other' end)
         end)) as [group_key],
         *
    from
        [some_table]
)
select
    [group_key],
    count(*) as [count]
from
    [cte]
group by
    [group_key]

簡単に言えば、機能的にはビューと同じですが、次のステートメントでのみ使用できます。ネストが回避されるため、派生テーブルのほとんどがより読みやすい代替手段と考えています。

この質問には関係ありませんが、それらは自分自身を参照し、そのようにして再帰クエリを構築するために使用できます。


@Quick Joe Smith:Exprxxxxxについても見たことがあるので、あなたは正しいと思います。ただし、式に手動で(Case ... end)式をOpTypeとして指定した場合、GROUP BY句でOpTypeフィールドを使用すると、列名が無効であるというエラーが発生します。
Dr. Drew

残念ながら、多くの場合、式を2回指定する唯一の方法は、CTE、ビュー、またはネストされたクエリの上記の方法のいずれかを使用することです。
クイックジョースミス

2
CROSS APPLYについても知らない限り。
Andriy M

cross applyこの場合の使用は少しストレッチであり、不要な自己結合を導入することでパフォーマンスに悪影響を与える可能性が非常に高くなります。
クイックジョースミス

2
あなたはその提案を「理解した」とは思わない。CROSS APPLYちょうど同じ行の列の別名を定義します。参加する必要はありません。例SELECT COUNT(*), hilo FROM master..spt_values CROSS APPLY (VALUES(high + low)) V(hilo) GROUP BY hilo
マーティン・スミス

1

パフォーマンスは1つの側面にすぎません。もう1つは保守性です。

個人的に、私は以下を行う傾向があります:

SELECT T.GroupingKey, SUM(T.value)
FROM
(
    SELECT 
        A.*
        (CASE WHEN A.type = 'Workover' THEN 'Workover' ELSE 
        (CASE WHEN substring(C.category, 2, 1) = 'D' THEN 'Drilling' WHEN substring(C.category, 2, 1) = 'C' THEN 'Completion' WHEN substring(C.category, 2, 1) = 'W' THEN 'Workover' ELSE 'Other' END)
        END) AS GroupingKey
    FROM Table AS A
) AS T

GROUP BY T.GroupingKey

更新:

入れ子にしたくない場合は、複雑な式を使用する必要があるテーブルごとにVIEWを作成できます。

CREATE VIEW TableExtended
AS 
SELECT 
    A.*
    (CASE WHEN A.type = 'Workover' THEN 'Workover' ELSE 
    (CASE WHEN substring(C.category, 2, 1) = 'D' THEN 'Drilling' WHEN substring(C.category, 2, 1) = 'C' THEN 'Completion' WHEN substring(C.category, 2, 1) = 'W' THEN 'Workover' ELSE 'Other' END)
    END) AS GroupingKey
FROM Table AS A

次に、追加のネストを行わずに選択を行うことができます。

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