web-dev-qa-db-ja.com

SQLを複製せずに集計関数を変更する方法(SQLを使用)

SQL Server 2016では、大きなGROUP BY ROLLUP内のさまざまな集計関数に従ってデータが処理されるシナリオがあります。 SQLインジェクションのリスクがなく、コンパイルを利用する方法でグループ化を記述するために使用する集計関数を指定するパラメーターを持つストアドプロシージャが必要です(これは重いストアドプロシージャです)。

私の考えは、特定の集計関数でデータのグループ化を要約するクエリのコレクションを使用することです。 (例:agg.DataMin、agg.DataMedian、agg.DataWeightedAverageなど)。次に、CTEのパラメーターでこれらを使用します

WITH AggData AS
(
   SELECT * FROM agg.DataMin WHERE @AggFunction = 1 
   UNION ALL
   SELECT * FROM agg.DataMedian WHERE @AggFunction = 2 
   UNION ALL
   SELECT * FROM agg.DataWeightedAverage WHERE @AggFunction = 3
)
SELECT ...

私の懸念は、クエリのパフォーマンスと業界のベストプラクティスです。データテーブルは妥当なサイズ(2ギガ以上)です。いくつかのleave-out集計のインラインテーブル値関数を含む、多くの集計クエリを追加する必要があります。

上記では、クエリ/テーブル値関数は、@AggFunctionWHERE条件に一致する場合にのみ実行されますか、それとも結果が返された後にすべて実行およびフィルタリングされますか?後者の場合、実行時に不要なクエリの評価を短絡する方法はありますか?また、私が見落としていたSQLでこれを実行するためのいくつかの標準的な方法はありますか?

1
Edmund

矛盾の検出couldキックインして、ステートメントの1つだけが実行されていることを確認します。私の簡単なテストでは、ステートメントレベルの再コンパイルのヒントがある限り実行しましたが、なぜリスクを冒すのでしょうか。例えば:

USE tempdb
GO

-- CREATE SCHEMA agg
--DROP TABLE agg.DataMin
--DROP TABLE agg.DataMedian
--DROP TABLE agg.DataWeightedAverage
--GO

CREATE TABLE agg.DataMin ( x INT PRIMARY KEY )
CREATE TABLE agg.DataMedian ( x INT PRIMARY KEY )
CREATE TABLE agg.DataWeightedAverage ( x INT PRIMARY KEY )
GO

INSERT INTO agg.DataMin ( x )
SELECT object_id FROM sys.all_objects

INSERT INTO agg.DataMedian ( x )
SELECT object_id FROM sys.all_objects WHERE type = 'P'

INSERT INTO agg.DataWeightedAverage ( x )
SELECT object_id FROM sys.all_objects WHERE type = 'X'
GO


-- Are there some situations when it wouldn't...
DECLARE @AggFunction INT = 1

;WITH AggData AS
(
   SELECT * FROM agg.DataMin WHERE @AggFunction = 1 
   UNION ALL
   SELECT * FROM agg.DataMedian WHERE @AggFunction = 2 
   UNION ALL
   SELECT * FROM agg.DataWeightedAverage WHERE @AggFunction = 3
)
SELECT *
FROM AggData
OPTION ( RECOMPILE )

私の結果: Recompile in action

この単純な例では、再コンパイルを使用して左側で1つのテーブルのみをスキャンし、再コンパイルを使用せずに右側で3つのテーブルをスキャンします。再コンパイルのヒントにより、オプティマイザはパラメータ値を「確認」し、それに応じて動作することができます。パラメータスニッフィングが使用されるストアドプロシージャでは、ステートメントレベルまたはstored-procレベルで同じ動作を取得するために、再コンパイルも必要になります。

しかし矛盾が検出されない状況がないかどうかはわかりません。そして、あなたはネガティブを証明することはできません。言い換えれば、再コンパイルしても矛盾検出が常に発生することを証明することはできません。再コンパイルしても発生しない未知の状況がいくつかあるかもしれません。過度の複雑さが頭に浮かびます。

また、例でCTEを使用することに実際の利点はないので、単純にしてみませんか? IF...THEN...ELSEを使用して単純な手続き型SQLを作成するだけで、ステートメントの1つだけが起動することが保証されます。

DECLARE @AggFunction INT = 99

IF @AggFunction = 1
   SELECT * FROM agg.DataMin
ELSE IF @AggFunction = 2
   SELECT * FROM agg.DataMedian
ELSE IF @AggFunction = 3
   SELECT * FROM agg.DataWeightedAverage
ELSE
    RAISERROR( 'Unknown value for parameter @AggFunction (%i).', 16, 1, @AggFunction )

あなたがそれにいる間にいくつかのパラメータチェックを追加します。これが、必要なときに1つのステートメントのみがコンパイルされ、安全で、実装が簡単であることを保証するという要件を満たしていることを願っています。

HTH

3
wBob