ピボットするSQLステートメントにDRY原則を適用する方法
-
19-08-2019 - |
質問
他の人がこの状況をどのように処理するのか、そしてこの状況に自分自身を繰り返さない(DRY)原則をどのように適用するのか疑問に思っています。
月を列として表示するために、T-SQLでCASEステートメントを常にPIVOTまたは作成していることに気付きました。通常、(1)日付フィールドと(2)値フィールドを含むいくつかのフィールドがあります。 ASPXページまたはReporting Servicesを通じてこれをユーザーに提示するとき、このパターンを使用するには、最後の右端の14列が必要です。
[年]、[Jan]、[Feb]、[Mar]、[Apr]、[May]、[Jun]、[Jul]、[Aug]、[Sep]、[Oct]、[Nov] 、[12月]、[合計]
年はintとしての年 そして、他のすべてのフィールドは、その月の合計値フィールドです([Total]は、その年の合計値フィールドです)。
これを処理するための再利用可能な方法を見つけたいです。すべての提案にオープン(T-SQL / ANSI SQL)
解決
これはまさにあなたが探しているものではありませんが、私は多くの反復的なUNPIVOT
を行いました。通常、標準化された命名法でこれをコード生成し、CTEを多用します:
WITH P AS (
SELECT Some Data
,[234] -- These are stats
,[235]
FROM Whatever
)
,FINAL_UNPIVOTED AS (
SELECT Some Data
,[STAT]
FROM P
UNPIVOT (
STAT FOR BASE IN ([234], [235])
) AS unpvt
WHERE STAT <> 0
)
SELECT Some Data
,CONVERT(int, FINAL_UNPIVOTED.[BASE]) AS [BASE]
,FINAL_UNPIVOTED.[STAT]
FROM FINAL_UNPIVOTED
テーブルまたはビューを検査し、次のようなコードを使用してコード生成できます:
DECLARE @sql_unpivot AS varchar(MAX)
SELECT @sql_unpivot = COALESCE(@sql_unpivot + ',', '') + COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'whatever'
そしてコードのテンプレート化:
SET @template = '
WITH P AS (
SELECT Some Data
,{@sql_unpivot}
FROM Whatever
)
,FINAL_UNPIVOTED AS (
SELECT Some Data
,[STAT]
FROM P
UNPIVOT (
STAT FOR BASE IN ({@sql_unpivot})
) AS unpvt
WHERE STAT <> 0
)
SELECT Some Data
,CONVERT(int, FINAL_UNPIVOTED.[BASE]) AS [BASE]
,FINAL_UNPIVOTED.[STAT]
FROM FINAL_UNPIVOTED
'
SET @sql = REPLACE(@template, '{@sql_unpivot}', @sql_unpivot)
etc。
もちろん、このコードを動的に実行したり、SPを作成したり、インラインで何かのメタデータを取得するために一時的に作成したビューまたはテーブルを交換することができます。
テーブル値関数および外部適用テクニックに関するコメントを参照してください。
他のヒント
遅れており、ここで明らかな何かを見逃しているかもしれませんが、各月の行を持つMonthsテーブルはそれを助けますか?
/* I leave year and month separate so you can use "real" Months or Fiscal Months */
CREATE FUNCTION [dbo].[fn_MonthValueColumns]
(
@year int,
@month int,
@measure int
)
RETURNS TABLE
AS
RETURN
(
SELECT @year as [Year],
CASE WHEN @month = 1 THEN @measure ELSE 0 END AS [Jan],
CASE WHEN @month = 2 THEN @measure ELSE 0 END AS [Feb],
CASE WHEN @month = 3 THEN @measure ELSE 0 END AS [Mar],
CASE WHEN @month = 4 THEN @measure ELSE 0 END AS [Apr],
CASE WHEN @month = 5 THEN @measure ELSE 0 END AS [May],
CASE WHEN @month = 6 THEN @measure ELSE 0 END AS [Jun],
CASE WHEN @month = 7 THEN @measure ELSE 0 END AS [Jul],
CASE WHEN @month = 8 THEN @measure ELSE 0 END AS [Aug],
CASE WHEN @month = 9 THEN @measure ELSE 0 END AS [Sep],
CASE WHEN @month = 10 THEN @measure ELSE 0 END AS [Oct],
CASE WHEN @month = 11 THEN @measure ELSE 0 END AS [Nov],
CASE WHEN @month = 12 THEN @measure ELSE 0 END AS [Dec],
@measure AS [Total]
)
/*
use a group by after your own CROSS APPLY to roll-up SUMs for the last 13 fields.
this function and a CROSS APPLY against 100000 records ran in 3 seconds.
for what I am doing, I can live with that performance.
*/
ビューの使用はどうですか?
常に同じテーブル/テーブルのグループを使用する場合、ビューが意味をなす場合があります。 警告:大きなテーブルの小さなセクションを使用する場合は、このようなビューに注意してください。