Vous recherchez une solution élégante (ou une solution quelconque) pour convertir les colonnes en lignes.
Voici un exemple: J'ai une table avec le schéma suivant:
[ID] [EntityID] [Indicator1] [Indicator2] [Indicator3] ... [Indicator150]
Voici ce que je veux obtenir comme résultat:
[ID] [EntityId] [IndicatorName] [IndicatorValue]
Et les valeurs de résultat seront:
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'
Etc..
Est-ce que ça a du sens? Avez-vous des suggestions sur où chercher et comment le faire dans T-SQL?
Vous pouvez utiliser la fonction UNPIVOT pour convertir les colonnes en lignes:
select id, entityId,
indicatorname,
indicatorvalue
from yourtable
unpivot
(
indicatorvalue
for indicatorname in (Indicator1, Indicator2, Indicator3)
) unpiv;
Veuillez noter que les types de données des colonnes pour lesquelles vous êtes non pivotés doivent être identiques, de sorte que vous devrez peut-être convertir les types de données avant d'appliquer l'impivot.
Vous pouvez également utiliser CROSS APPLY
avec UNION ALL pour convertir les colonnes:
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);
En fonction de votre version de SQL Server, vous pouvez même utiliser CROSS APPLY avec la clause VALUES:
select id, entityid,
indicatorname,
indicatorvalue
from yourtable
cross apply
(
values
('Indicator1', Indicator1),
('Indicator2', Indicator2),
('Indicator3', Indicator3),
('Indicator4', Indicator4)
) c (indicatorname, indicatorvalue);
Enfin, si vous avez 150 colonnes à décomposer et que vous ne souhaitez pas coder de manière irréversible l'intégralité de la requête, vous pouvez générer l'instruction SQL à l'aide de SQL dynamique:
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;
eh bien, si vous avez 150 colonnes, alors je pense que UNPIVOT n'est pas une option. Donc, vous pouvez utiliser l'astuce 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%'
Vous pouvez également écrire du SQL dynamique, mais j'aime davantage xml. Pour le SQL dynamique, vous devez avoir le droit de sélectionner des données directement à partir d'une table, ce qui n'est pas toujours une option.
UPDATE
Comme il ya une grosse flamme dans les commentaires, je pense que je vais ajouter quelques avantages et inconvénients du code XML/dynamique. Je vais essayer d'être aussi objectif que possible et de ne pas parler d'élégance et de laideur. Si vous avez d'autres avantages et inconvénients, modifiez la réponse ou écrivez des commentaires.
contre
pros
inserted
et deleted
tables à l'intérieur de votre trigger (impossible avec dynamic du tout);Juste pour aider les nouveaux lecteurs, j'ai créé un exemple pour mieux comprendre la réponse de @ bluefeet sur 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;
J'avais besoin d'une solution pour convertir des colonnes en lignes dans Microsoft SQL Server, sans connaître les noms de colonnes (utilisés dans le déclencheur) ni sans SQL dynamique (SQL dynamique est trop lent pour être utilisé dans un déclencheur).
J'ai finalement trouvé cette solution qui fonctionne bien:
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)
Comme vous pouvez le constater, je convertis la ligne en XML (Subquery select i, * pour xml raw, cela convertit toutes les colonnes en une seule colonne xml).
Ensuite, je CROIS APPLIQUE une fonction à chaque attribut XML de cette colonne, de manière à obtenir une ligne par attribut.
Globalement, cela convertit les colonnes en lignes, sans connaître les noms des colonnes et sans utiliser SQL dynamique. C'est assez rapide pour mon but.
(Edit: Je viens de voir Roman Pekar répondre ci-dessus, qui fait la même chose. J'ai utilisé le déclencheur SQL dynamique avec des curseurs en premier, qui était 10 à 100 fois plus lent que cette solution, mais peut-être que cela a été causé par le curseur, pas par la dynamique sql. En tout cas, cette solution est très simple, universelle, donc c’est définitivement une option).
Je laisse ce commentaire ici, car je veux faire référence à cette explication dans mon message sur le déclencheur d'audit complet, que vous pouvez trouver ici: 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
Voici une autre méthode pour les colonnes aux lignes, combien de table et combien de colonnes que vous avez, n'est pas important. Il suffit de définir les paramètres et d’obtenir le résultat. J'ai écrit ceci, car parfois j'ai besoin du résultat d'une table A (qui est un ensemble de résultats de colonne), en tant que champs d'une autre table B (qui doit être des champs de lignes). Dans ce cas, je ne sais pas combien de champs j'ai définis pour ma table B.
Juste parce que je ne l'ai pas vu mentionné.
Si 2016+, il existe encore une autre option permettant de dé-dynamiser dynamiquement des données sans utiliser réellement le SQL dynamique.
Exemple
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
Retours
ID Item Value
1 Col1 A
1 Col2 B
2 Col1 R
2 Col2 C
3 Col1 X
3 Col2 D