J'ai lu ce qui se trouve sur les tableaux croisés dynamiques MS et j'ai toujours du mal à comprendre.
J'ai une table temporaire en cours de création, nous dirons que la colonne 1 est un numéro de magasin, et la colonne 2 est un numéro de semaine et enfin la colonne 3 est un total d'un type. De plus, les numéros de semaine sont dynamiques, les numéros de magasins sont statiques.
Store Week xCount
------- ---- ------
102 1 96
101 1 138
105 1 37
109 1 59
101 2 282
102 2 212
105 2 78
109 2 97
105 3 60
102 3 123
101 3 220
109 3 87
Je voudrais qu'il se présente comme un tableau croisé dynamique, comme ceci:
Store 1 2 3 4 5 6....
-----
101 138 282 220
102 96 212 123
105 37
109
Stockez les chiffres en bas et les semaines en haut.
Si vous utilisez SQL Server 2005+, vous pouvez utiliser la fonction PIVOT
pour transformer les données des lignes en colonnes.
Il semble que vous ayez besoin d'utiliser SQL dynamique si les semaines ne sont pas connues, mais il est plus facile de voir le code correct à l'aide d'une version initialement codée.
Tout d’abord, voici quelques définitions rapides de tableaux et données à utiliser:
CREATE TABLE #yt
(
[Store] int,
[Week] int,
[xCount] int
);
INSERT INTO #yt
(
[Store],
[Week], [xCount]
)
VALUES
(102, 1, 96),
(101, 1, 138),
(105, 1, 37),
(109, 1, 59),
(101, 2, 282),
(102, 2, 212),
(105, 2, 78),
(109, 2, 97),
(105, 3, 60),
(102, 3, 123),
(101, 3, 220),
(109, 3, 87);
Si vos valeurs sont connues, alors vous coderez en dur la requête:
select *
from
(
select store, week, xCount
from yt
) src
pivot
(
sum(xcount)
for week in ([1], [2], [3])
) piv;
Voir Démo SQL
Ensuite, si vous devez générer le numéro de semaine de manière dynamique, votre code sera:
DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX)
select @cols = STUFF((SELECT ',' + QUOTENAME(Week)
from yt
group by Week
order by Week
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query = 'SELECT store,' + @cols + ' from
(
select store, week, xCount
from yt
) x
pivot
(
sum(xCount)
for week in (' + @cols + ')
) p '
execute(@query);
Voir Démo SQL .
La version dynamique génère la liste de week
nombres à convertir en colonnes. Les deux donnent le même résultat:
| STORE | 1 | 2 | 3 |
---------------------------
| 101 | 138 | 282 | 220 |
| 102 | 96 | 212 | 123 |
| 105 | 37 | 78 | 60 |
| 109 | 59 | 97 | 87 |
Ceci est pour dynamique # de semaines.
Exemple complet ici: SQL Dynamic Pivot
DECLARE @DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE @ColumnName AS NVARCHAR(MAX)
--Get distinct values of the PIVOT Column
SELECT @ColumnName= ISNULL(@ColumnName + ',','') + QUOTENAME(Week)
FROM (SELECT DISTINCT Week FROM #StoreSales) AS Weeks
--Prepare the PIVOT query using the dynamic
SET @DynamicPivotQuery =
N'SELECT Store, ' + @ColumnName + '
FROM #StoreSales
PIVOT(SUM(xCount)
FOR Week IN (' + @ColumnName + ')) AS PVTTable'
--Execute the Dynamic Pivot Query
EXEC sp_executesql @DynamicPivotQuery
J'ai déjà réalisé la même chose en utilisant des sous-requêtes. Ainsi, si votre table d'origine s'appelait StoreCountsByWeek et que vous disposiez d'une table séparée répertoriant les ID de magasin, elle ressemblerait à ceci:
SELECT StoreID,
Week1=(SELECT ISNULL(SUM(xCount),0) FROM StoreCountsByWeek WHERE StoreCountsByWeek.StoreID=Store.StoreID AND Week=1),
Week2=(SELECT ISNULL(SUM(xCount),0) FROM StoreCountsByWeek WHERE StoreCountsByWeek.StoreID=Store.StoreID AND Week=2),
Week3=(SELECT ISNULL(SUM(xCount),0) FROM StoreCountsByWeek WHERE StoreCountsByWeek.StoreID=Store.StoreID AND Week=3)
FROM Store
ORDER BY StoreID
L'un des avantages de cette méthode est que la syntaxe est plus claire et qu'il est plus facile de joindre des tables à d'autres tables pour extraire d'autres champs dans les résultats.
Mes résultats anecdotiques sont que l'exécution de cette requête sur plusieurs milliers de lignes a été complétée en moins d'une seconde et que j'avais en fait 7 sous-requêtes. Mais comme indiqué dans les commentaires, il est plus onéreux de le faire de cette façon. Veillez donc à utiliser cette méthode si vous prévoyez de l'exécuter sur de grandes quantités de données.
Voici ce que vous pouvez faire:
SELECT *
FROM yourTable
PIVOT (MAX(xCount)
FOR Week in ([1],[2],[3],[4],[5],[6],[7])) AS pvt
J'écris un sp qui pourrait être utile à cette fin. Essentiellement, il fait pivoter une table et renvoie une nouvelle table pivotée ou ne renvoie que le jeu de données. Voici le moyen de l'exécuter:
Exec dbo.rs_pivot_table @schema=dbo,@table=table_name,@column=column_to_pivot,@agg='sum([column_to_agg]),avg([another_column_to_agg]),',
@sel_cols='column_to_select1,column_to_select2,column_to_select1',@new_table=returned_table_pivoted;
notez svp que, dans le paramètre @agg, les noms de colonne doivent être avec '['
et que le paramètre doit se terminer par une virgule ','
SP
Create Procedure [dbo].[rs_pivot_table]
@schema sysname=dbo,
@table sysname,
@column sysname,
@agg nvarchar(max),
@sel_cols varchar(max),
@new_table sysname,
@add_to_col_name sysname=null
As
--Exec dbo.rs_pivot_table dbo,##TEMPORAL1,tip_liq,'sum([val_liq]),sum([can_liq]),','cod_emp,cod_con,tip_liq',##TEMPORAL1PVT,'hola';
Begin
Declare @query varchar(max)='';
Declare @aggDet varchar(100);
Declare @opp_agg varchar(5);
Declare @col_agg varchar(100);
Declare @pivot_col sysname;
Declare @query_col_pvt varchar(max)='';
Declare @full_query_pivot varchar(max)='';
Declare @ind_tmpTbl int; --Indicador de tabla temporal 1=tabla temporal global 0=Tabla fisica
Create Table #pvt_column(
pivot_col varchar(100)
);
Declare @column_agg table(
opp_agg varchar(5),
col_agg varchar(100)
);
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(@table) AND type in (N'U'))
Set @ind_tmpTbl=0;
ELSE IF OBJECT_ID('tempdb..'+ltrim(rtrim(@table))) IS NOT NULL
Set @ind_tmpTbl=1;
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(@new_table) AND type in (N'U')) OR
OBJECT_ID('tempdb..'+ltrim(rtrim(@new_table))) IS NOT NULL
Begin
Set @query='DROP TABLE '+@new_table+'';
Exec (@query);
End;
Select @query='Select distinct '+@column+' From '+(case when @ind_tmpTbl=1 then 'tempdb.' else '' end)+@schema+'.'+@table+' where '+@column+' is not null;';
Print @query;
Insert into #pvt_column(pivot_col)
Exec (@query)
While charindex(',',@agg,1)>0
Begin
Select @aggDet=Substring(@agg,1,charindex(',',@agg,1)-1);
Insert Into @column_agg(opp_agg,col_agg)
Values(substring(@aggDet,1,charindex('(',@aggDet,1)-1),ltrim(rtrim(replace(substring(@aggDet,charindex('[',@aggDet,1),charindex(']',@aggDet,1)-4),')',''))));
Set @agg=Substring(@agg,charindex(',',@agg,1)+1,len(@agg))
End
Declare cur_agg cursor read_only forward_only local static for
Select
opp_agg,col_agg
from @column_agg;
Open cur_agg;
Fetch Next From cur_agg
Into @opp_agg,@col_agg;
While @@fetch_status=0
Begin
Declare cur_col cursor read_only forward_only local static for
Select
pivot_col
From #pvt_column;
Open cur_col;
Fetch Next From cur_col
Into @pivot_col;
While @@fetch_status=0
Begin
Select @query_col_pvt='isnull('+@opp_agg+'(case when '+@column+'='+quotename(@pivot_col,char(39))+' then '+@col_agg+
' else null end),0) as ['+lower(Replace(Replace(@opp_agg+'_'+convert(varchar(100),@pivot_col)+'_'+replace(replace(@col_agg,'[',''),']',''),' ',''),'&',''))+
(case when @add_to_col_name is null then space(0) else '_'+isnull(ltrim(rtrim(@add_to_col_name)),'') end)+']'
print @query_col_pvt
Select @full_query_pivot=@full_query_pivot+@query_col_pvt+', '
--print @full_query_pivot
Fetch Next From cur_col
Into @pivot_col;
End
Close cur_col;
Deallocate cur_col;
Fetch Next From cur_agg
Into @opp_agg,@col_agg;
End
Close cur_agg;
Deallocate cur_agg;
Select @full_query_pivot=substring(@full_query_pivot,1,len(@full_query_pivot)-1);
Select @query='Select '+@sel_cols+','+@full_query_pivot+' into '+@new_table+' From '+(case when @ind_tmpTbl=1 then 'tempdb.' else '' end)+
@schema+'.'+@table+' Group by '+@sel_cols+';';
print @query;
Exec (@query);
End;
GO
Voici un exemple d'exécution:
Exec dbo.rs_pivot_table @schema=dbo,@table=##TEMPORAL1,@column=tip_liq,@agg='sum([val_liq]),avg([can_liq]),',@sel_cols='cod_emp,cod_con,tip_liq',@new_table=##TEMPORAL1PVT;
alors Select * From ##TEMPORAL1PVT
renverrait:
select * from (select name, ID from Empoyee) Visits
pivot(sum(ID) for name
in ([Emp1],
[Emp2],
[Emp3]
) ) as pivottable;
Donnez-vous simplement une idée de la façon dont d'autres bases de données résolvent ce problème. DolphinDB
possède également un support intégré pour le pivotement et le SQL semble beaucoup plus intuitif et ordonné. C'est aussi simple que de spécifier la colonne clé (Store
), la colonne pivotante (Week
) et la mesure calculée (sum(xCount)
).
//prepare a 10-million-row table
n=10000000
t=table(Rand(100, n) + 1 as Store, Rand(54, n) + 1 as Week, Rand(100, n) + 1 as xCount)
//use pivot clause to generate a pivoted table pivot_t
pivot_t = select sum(xCount) from t pivot by Store, Week
DolphinDB est une base de données haute performance en colonnes. Le calcul dans la démo coûte aussi peu que 546 ms sur un ordinateur portable Dell xps (processeur i7). Pour plus de détails, veuillez vous référer au manuel en ligne DolphinDB https://www.dolphindb.com/help/index.html?pivotby.html
Voici une révision de la réponse de @Tayrn ci-dessus qui pourrait vous aider à comprendre un peu plus facilement le pivotement:
Ce n’est peut-être pas la meilleure façon de procéder, mais c’est ce qui m’a aidé à comprendre pourquoi je fais des tableaux croisés dynamiques.
ID = lignes que vous souhaitez faire pivoter
MY_KEY = la colonne que vous sélectionnez dans votre table d'origine et qui contient les noms de colonne que vous souhaitez faire pivoter.
VAL = la valeur que vous voulez renvoyer sous chaque colonne.
MAX (VAL) => Peut être remplacé par d'autres fonctions d'agrégat. SUM (VAL), MIN (VAL), ETC ...
DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX)
select @cols = STUFF((SELECT ',' + QUOTENAME(MY_KEY)
from yt
group by MY_KEY
order by MY_KEY ASC
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query = 'SELECT ID,' + @cols + ' from
(
select ID, MY_KEY, VAL
from yt
) x
pivot
(
sum(VAL)
for MY_KEY in (' + @cols + ')
) p '
execute(@query);