列を行に変換するための洗練された(または任意の)解決策を探してください。
例を示します。次のようなスキーマを持つテーブルがあります。
[ID] [EntityID] [Indicator1] [Indicator2] [Indicator3] ... [Indicator150]
結果として取得したいものは次のとおりです。
[ID] [EntityId] [IndicatorName] [IndicatorValue]
そして結果の値は次のようになります。
1 1 'Indicator1' 'Value of Indicator 1 for entity 1'
2 1 'Indicator2' 'Value of Indicator 2 for entity 1'
3 1 'Indicator3' 'Value of Indicator 3 for entity 1'
4 2 'Indicator1' 'Value of Indicator 1 for entity 2'
等々..
これは意味がありますか? T-SQLでどこを見てどこでそれを実現するかについて何か提案はありますか?
UNPIVOT 関数を使用して列を行に変換できます。
select id, entityId,
indicatorname,
indicatorvalue
from yourtable
unpivot
(
indicatorvalue
for indicatorname in (Indicator1, Indicator2, Indicator3)
) unpiv;
ピボット解除する列のデータ型は同じである必要があるので、ピボット解除を適用する前にデータ型を変換する必要があるかもしれません。
CROSS APPLY
をUNION ALLとともに使用して列を変換することもできます。
select id, entityid,
indicatorname,
indicatorvalue
from yourtable
cross apply
(
select 'Indicator1', Indicator1 union all
select 'Indicator2', Indicator2 union all
select 'Indicator3', Indicator3 union all
select 'Indicator4', Indicator4
) c (indicatorname, indicatorvalue);
使用しているSQL Serverのバージョンによっては、VALUES句を指定してCROSS APPLYを使用することもできます。
select id, entityid,
indicatorname,
indicatorvalue
from yourtable
cross apply
(
values
('Indicator1', Indicator1),
('Indicator2', Indicator2),
('Indicator3', Indicator3),
('Indicator4', Indicator4)
) c (indicatorname, indicatorvalue);
最後に、ピボットを解除する150列があり、クエリ全体をハードコードしたくない場合は、動的SQLを使用してsqlステートメントを生成できます。
DECLARE @colsUnpivot AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX)
select @colsUnpivot
= stuff((select ','+quotename(C.column_name)
from information_schema.columns as C
where C.table_name = 'yourtable' and
C.column_name like 'Indicator%'
for xml path('')), 1, 1, '')
set @query
= 'select id, entityId,
indicatorname,
indicatorvalue
from yourtable
unpivot
(
indicatorvalue
for indicatorname in ('+ @colsunpivot +')
) u'
exec sp_executesql @query;
150列あるのなら、UNPIVOTは選択肢ではないと思います。だからあなたはXMLトリックを使用することができます
;with CTE1 as (
select ID, EntityID, (select t.* for xml raw('row'), type) as Data
from temp1 as t
), CTE2 as (
select
C.id, C.EntityID,
F.C.value('local-name(.)', 'nvarchar(128)') as IndicatorName,
F.C.value('.', 'nvarchar(max)') as IndicatorValue
from CTE1 as c
outer apply c.Data.nodes('row/@*') as F(C)
)
select * from CTE2 where IndicatorName like 'Indicator%'
動的SQLを書くこともできますが、私はxmlがもっと好きです。動的SQLの場合は、テーブルから直接データを選択する権限を持っている必要があり、それは常に選択肢とは限りません。
_アップデート_
コメントに大きな炎があるので、xml /動的SQLの長所と短所をいくつか追加します。私は出来る限り客観的になるように努力しますが、優雅さと醜さは言うまでもありません。他にも長所と短所がある場合は、答えを編集するか、コメントを書いてください。
短所
長所
inserted
テーブルとdeleted
テーブルを問い合わせることができます(動的では不可能です)。新しい読者を助けるために、@ bluefeetのUNPIVOTに関する回答をよりよく理解するための例を作成しました。
SELECT id
,entityId
,indicatorname
,indicatorvalue
FROM (VALUES
(1, 1, 'Value of Indicator 1 for entity 1', 'Value of Indicator 2 for entity 1', 'Value of Indicator 3 for entity 1'),
(2, 1, 'Value of Indicator 1 for entity 2', 'Value of Indicator 2 for entity 2', 'Value of Indicator 3 for entity 2'),
(3, 1, 'Value of Indicator 1 for entity 3', 'Value of Indicator 2 for entity 3', 'Value of Indicator 3 for entity 3'),
(4, 2, 'Value of Indicator 1 for entity 4', 'Value of Indicator 2 for entity 4', 'Value of Indicator 3 for entity 4')
) AS Category(ID, EntityId, Indicator1, Indicator2, Indicator3)
UNPIVOT
(
indicatorvalue
FOR indicatorname IN (Indicator1, Indicator2, Indicator3)
) UNPIV;
Microsoft SQL Serverで列を行に変換するための解決策が必要でした。(トリガーで使用される)列名を認識せずに、動的SQLを使用しないでください(動的SQLはトリガーで使用するには遅すぎます)。
私はついにこの解決策を見つけました、それはうまくいきます:
SELECT
insRowTbl.PK,
insRowTbl.Username,
attr.insRow.value('local-name(.)', 'nvarchar(128)') as FieldName,
attr.insRow.value('.', 'nvarchar(max)') as FieldValue
FROM ( Select
i.ID as PK,
i.LastModifiedBy as Username,
convert(xml, (select i.* for xml raw)) as insRowCol
FROM inserted as i
) as insRowTbl
CROSS APPLY insRowTbl.insRowCol.nodes('/row/@*') as attr(insRow)
ご覧のとおり、行をXMLに変換します(xml rawに対してSubquery select i、*、これはすべての列を1つのxml列に変換します)。
次に、この列の各XML属性に関数をクロス適用して、属性ごとに1行を取得します。
全体として、これは列名を知らなくても動的SQLを使用せずに、列を行に変換します。私の目的には十分速いです。
(編集:私はちょうど同じことをしている、上でRoman Pekarの答えを見た、私は最初にカーソルで動的SQLトリガーを使用した。これはこのソリューションよりも10から100倍遅くなっていた。動的SQLとにかく、この解決策は非常に単純で普遍的なので、その決定的な選択肢です。
私はこのコメントをこの場所に残しています、なぜなら私はあなたがここで見つけることができるという完全な監査トリガーについての私のポストでこの説明を参照したいからです。 https://stackoverflow.com/a/43800286/4160788 =
DECLARE @TableName nvarchar(50)
DECLARE column_to_row CURSOR FOR
--List of tables that we want to unpivot columns as row
SELECT DISTINCT t.name FROM sys.tables t
JOIN sys.schemas s ON t.schema_id=t.schema_id
WHERE t.name like '%_CT%'
AND s.name='cdc'
OPEN column_to_row
FETCH NEXT FROM column_to_row INTO @TableName
WHILE @@FETCH_STATUS = 0
BEGIN
DECLARE @script nvarchar(max) = null
DECLARE @columns nvarchar(2000) = null
-- keep the table's column list
select @columns = COALESCE(@columns + ',','') + c.name from sys.tables t
join sys.columns c on t.object_id = c.object_id
where t.name = @TableName
set @script = 'SELECT '+@columns+' FROM [cdc].['+@TableName+'] (nolock)'
--print (@script)
exec (@script)
FETCH NEXT FROM column_to_row INTO @TableName
END
CLOSE column_to_row
DEALLOCATE column_to_row
これは、列から行への別の方法です。テーブルの数と列の数は重要ではありません。パラメータを設定して結果を取得するだけです。私はこれを書きました。時々私は別のテーブルBのフィールドとして(行フィールドでなければならない)テーブルA(列の結果セットです)の結果が必要だからです。その場合、テーブルBに設定したフィールド数がわかりません。
私がそれが言及されているのを見なかったという理由だけで。
2016+の場合は、動的SQLを実際に使用せずにデータを動的にアンピボットするもう1つの方法があります。
例
Declare @YourTable Table ([ID] varchar(50),[Col1] varchar(50),[Col2] varchar(50))
Insert Into @YourTable Values
(1,'A','B')
,(2,'R','C')
,(3,'X','D')
Select A.[ID]
,Item = B.[Key]
,Value = B.[Value]
From @YourTable A
Cross Apply ( Select *
From OpenJson((Select A.* For JSON Path,Without_Array_Wrapper ))
Where [Key] not in ('ID','Other','Columns','ToExclude')
) B
戻り値
ID Item Value
1 Col1 A
1 Col2 B
2 Col1 R
2 Col2 C
3 Col1 X
3 Col2 D