テーブルを単一の行に「フラット化」する最良の方法は何ですか?
たとえば、次の表の場合:
+-----+-------+-------------+------------------+
| Id | hProp | iDayOfMonth | dblTargetPercent |
+-----+-------+-------------+------------------+
| 117 | 10 | 5 | 0.1400 |
| 118 | 10 | 10 | 0.0500 |
| 119 | 10 | 15 | 0.0100 |
| 120 | 10 | 20 | 0.0100 |
+-----+-------+-------------+------------------+
次の表を作成したいと思います。
+-------+--------------+-------------------+--------------+-------------------+--------------+-------------------+--------------+-------------------+
| hProp | iDateTarget1 | dblPercentTarget1 | iDateTarget2 | dblPercentTarget2 | iDateTarget3 | dblPercentTarget3 | iDateTarget4 | dblPercentTarget4 |
+-------+--------------+-------------------+--------------+-------------------+--------------+-------------------+--------------+-------------------+
| 10 | 5 | 0.14 | 10 | 0.05 | 15 | 0.01 | 20 | 0.01 |
+-------+--------------+-------------------+--------------+-------------------+--------------+-------------------+--------------+-------------------+
私はピボットを使用してこれを何とかして元のテーブルに何度か再結合しましたが、もっと良い方法があるとかなり確信しています。これは期待どおりに動作します:
select
X0.hProp,
X0.iDateTarget1,
X1.dblTargetPercent [dblPercentTarget1],
X0.iDateTarget2,
X2.dblTargetPercent [dblPercentTarget2],
X0.iDateTarget3,
X3.dblTargetPercent [dblPercentTarget3],
X0.iDateTarget4,
X4.dblTargetPercent [dblPercentTarget4]
from (
select
hProp,
max([1]) [iDateTarget1],
max([2]) [iDateTarget2],
max([3]) [iDateTarget3],
max([4]) [iDateTarget4]
from (
select
*,
rank() over (partition by hProp order by iWeek) rank#
from [Table X]
) T
pivot (max(iWeek) for rank# in ([1],[2],[3], [4])) pv
group by hProp
) X0
left join [Table X] X1 on X1.hprop = X0.hProp and X1.iWeek = X0.iDateTarget1
left join [Table X] X2 on X2.hprop = X0.hProp and X2.iWeek = X0.iDateTarget2
left join [Table X] X3 on X3.hprop = X0.hProp and X3.iWeek = X0.iDateTarget3
left join [Table X] X4 on X4.hprop = X0.hProp and X4.iWeek = X0.iDateTarget4
複数の結合を行わずに必要な結果セットを取得する1つの方法を次に示します。もう少し設定が必要で、1つではなく2つのピボット操作を使用しますが、複数の結合を回避します。
私はそれを調べなければならなかったことを認めますが、ケン・オボンは素晴らしい記事を持っていました。 https://blogs.msdn.Microsoft.com/kenobonn/2009/03/22/pivot-on-two-or-more-fields-in-sql-server/
/** Build up a Table to work with. **/
DECLARE @T TABLE
(
ID INT NOT NULL PRIMARY KEY
, hProp INT NOT NULL
, iDayOfMonth INT NOT NULL
, dblTargetPercent DECIMAL(6,4) NOT NULL
)
INSERT INTO @T
(ID, hProp, iDayOfMonth, dblTargetPercent)
VALUES (117,10,5,0.1400)
, (118, 10, 10, 0.0500)
, (119, 10, 15, 0.0100)
, (120, 10, 20, 0.0100)
/** Create a CTE and give us predictable names to work with for
date and percentage
**/
;WITH CTE_Rank AS
(
SELECT ID
, hProp
, iDayOfMonth
, dblTargetPercent
, sDateName = 'iDateTarget' + CAST(DENSE_RANK() OVER (PARTITION BY hPRop ORDER BY iDayOfMonth) AS VARCHAR(10))
, sPercentName = 'dblPercentTarget' + CAST(DENSE_RANK() OVER (PARTITION BY hPRop ORDER BY iDayOfMonth) AS VARCHAR(10))
FROM @T
)
SELECT hProp
, iDateTarget1 = MAX(iDateTarget1)
, dblPercentTarget1 = MAX(dblPercentTarget1)
, iDateTarget2 = MAX(iDateTarget2)
, dblPercentTarget2 = MAX(dblPercentTarget2)
, iDateTarget3 = MAX(iDateTarget3)
, dblPercentTarget3 = MAX(dblPercentTarget3)
, iDateTarget4 = MAX(iDateTarget4)
, dblPercentTarget4 = MAX(dblPercentTarget4)
FROM CTE_Rank AS R
PIVOT(MAX(iDayOfMonth) FOR sDateName IN ([iDateTarget1], [iDateTarget2], [iDateTarget3], [iDateTarget4])) AS DayOfMonthName
PIVOT(MAX(dblTargetPercent) FOR sPercentName IN (dblPercentTarget1, dblPercentTarget2, dblPercentTarget3, dblPercentTarget4)) AS TargetPercentName
GROUP BY hProp
与えられた:
DECLARE @T table
(
ID integer NOT NULL PRIMARY KEY,
hProp integer NOT NULL,
iDayOfMonth integer NOT NULL,
dblTargetPercent decimal(6,4) NOT NULL
);
INSERT @T
(ID, hProp, iDayOfMonth, dblTargetPercent)
VALUES
(117, 10, 05, 0.1400),
(118, 10, 10, 0.0500),
(119, 10, 15, 0.0100),
(120, 10, 20, 0.0100);
手動ピボットで記述された結果を得ることができます:
WITH Ranked AS
(
SELECT
T.*,
rn = ROW_NUMBER() OVER (
PARTITION BY T.hProp
ORDER BY T.iDayOfMonth)
FROM @T AS T
)
SELECT
R.hProp,
iDateTarget1 = MAX(CASE WHEN R.rn = 1 THEN R.iDayOfMonth END),
dblPercentTarget1 = MAX(CASE WHEN R.rn = 1 THEN R.dblTargetPercent END),
iDateTarget2 = MAX(CASE WHEN R.rn = 2 THEN R.iDayOfMonth END),
dblPercentTarget1 = MAX(CASE WHEN R.rn = 2 THEN R.dblTargetPercent END),
iDateTarget3 = MAX(CASE WHEN R.rn = 3 THEN R.iDayOfMonth END),
dblPercentTarget3 = MAX(CASE WHEN R.rn = 3 THEN R.dblTargetPercent END),
iDateTarget4 = MAX(CASE WHEN R.rn = 4 THEN R.iDayOfMonth END),
dblPercentTarget4 = MAX(CASE WHEN R.rn = 4 THEN R.dblTargetPercent END)
FROM Ranked AS R
GROUP BY
R.hProp;
db <> fiddle ここ
私はクロス適用を使用してピボットを解除してから、単一のピボットを使用することを好みます。 2つの値が同じ列(型)にマップされるため、この手法には問題があり、適切な型に再キャストするために、途中で処理する必要があります。しかし、私の経験では、この方法はより大きなデータセットで非常にうまく機能します。 group byがないことに注意してください。
同じソースデータを使用する:
;with src (hProp, iDayOfMonth, dblTargetPercent, rw) as (
select hProp, iDayOfMonth, dblTargetPercent,
ROW_NUMBER() over (partition by hProp order by iDayOfMonth)
from @T
)
,unpvt(hProp, typ, val) as (
select hprop, ca.typ + ltrim(rw), ca.val from src
cross apply (values (iDayOfMonth, 'iDayOfMonth'),(dblTargetPercent, 'dblTargetPercent')) ca (val, typ)
)
select *
from unpvt
pivot (max(val) for typ in ([iDayOfMonth1],[dblTargetPercent1],[iDayOfMonth2],[dblTargetPercent2],
[iDayOfMonth3],[dblTargetPercent3],[iDayOfMonth4],[dblTargetPercent4]))p
次のコードを使用して400万行のソリューションをテストし、テストデータを生成しました。
if object_id(N'tempdb..#T',N'U') is not null drop table #T
create table #T(ID int identity(1,1) primary key clustered,
hProp int not null, iDayOfMonth int not null, dblTargetPercent decimal(6,4) not null)
;with src(hProp) as (
select 1 union all
select hProp+1 from src where hProp+1 <= 1000000
)
,dta(iDayOfMonth, dblTargetPercent) as (
select 5, 0.1400 union all
select 10, 0.0500 union all
select 15, 0.0100 union all
select 20, 0.0100
)
insert #T(hProp, iDayOfMonth, dblTargetPercent)
select hProp, iDayOfMonth, dblTargetPercent
from src, dta
option(maxrecursion 0)
ポールホワイトソリューションが最速で、次が私のソリューションです。古い学校が勝ちます!