web-dev-qa-db-fra.com

SQL Server: Transposer des lignes en colonnes

En plus d'écrire le curseur en lisant chaque ligne et de le remplir en colonnes, une autre alternative si je dois transposer chaque ligne en colonnes?

TimeSeconds TagID Value
1378700244  A1    3.75
1378700245  A1    30
1378700304  A1    1.2
1378700305  A2    56
1378700344  A2    11
1378700345  A3    0.53
1378700364  A1    4
1378700365  A1    14.5
1378700384  A1    144
1378700384  A4    10

Le nombre de colonnes n'est pas fixe.

Sortie: Je viens d'affecter n/a en tant qu'espace réservé pour aucune donnée dans cette intersection.

TimeSec     A1    A2    A3    A4
1378700244  3.75  n/a   n/a   n/a
1378700245  30    n/a   n/a   n/a
1378700304  1.2   n/a   n/a   n/a
1378700305  n/a   56    n/a   n/a
1378700344  n/a   11    n/a   n/a
1378700345  n/a   n/a   0.53  n/a
1378700364  n/a   n/a   n/a   4
1378700365  14.5  n/a   n/a   n/a
1378700384  144   n/a   n/a   10

J'espère que vous pourrez partager avec moi quelques conseils. Merci.

27
user3015739

Une façon de le faire si tagID les valeurs sont connues dès le départ est d’utiliser l’agrégation conditionnelle

SELECT TimeSeconds,
       COALESCE(MAX(CASE WHEN TagID = 'A1' THEN Value END), 'n/a') A1,
       COALESCE(MAX(CASE WHEN TagID = 'A2' THEN Value END), 'n/a') A2,
       COALESCE(MAX(CASE WHEN TagID = 'A3' THEN Value END), 'n/a') A3,
       COALESCE(MAX(CASE WHEN TagID = 'A4' THEN Value END), 'n/a') A4
  FROM table1
 GROUP BY TimeSeconds

ou si vous êtes d'accord avec NULL valeurs au lieu de 'n/a'

SELECT TimeSeconds,
       MAX(CASE WHEN TagID = 'A1' THEN Value END) A1,
       MAX(CASE WHEN TagID = 'A2' THEN Value END) A2,
       MAX(CASE WHEN TagID = 'A3' THEN Value END) A3,
       MAX(CASE WHEN TagID = 'A4' THEN Value END) A4
  FROM table1
 GROUP BY TimeSeconds

ou avec PIVOT

SELECT TimeSeconds, A1, A2, A3, A4
  FROM
(
  SELECT TimeSeconds, TagID, Value
    FROM table1
) s
PIVOT
(
  MAX(Value) FOR TagID IN (A1, A2, A3, A4)
) p

Sortie (avec NULLs):

 TimeSeconds A1 A2 A3 A4 
 ----------- ------- ------ ----- ----- 
 1378700244 3,75 NULL NULL NULL 
 1378700245 30,00 NULL NULL NULL 
 1378700304 1.20 NULL NULL 
 1378700305 NULL 56,00 NULL NULL 
 1378700305 NULL 
 .____.] 1378700345 NULL NULL 0.53 NULL 
 1378700364 4.00 NULL NULL NULL 
 1378700365 14.50 NULL NULL 
 1378700384 144.00 NULL NULL 10.00 

Si vous devez comprendre TagID valeurs de manière dynamique, utilisez SQL dynamique

DECLARE @cols NVARCHAR(MAX), @sql NVARCHAR(MAX)

SET @cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(TagID)
            FROM Table1
            ORDER BY 1
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)'),1,1,'')

SET @sql = 'SELECT TimeSeconds, ' + @cols + '
              FROM
            (
              SELECT TimeSeconds, TagID, Value
                FROM table1
            ) s
            PIVOT
            (
              MAX(Value) FOR TagID IN (' + @cols + ')
            ) p'

EXECUTE(@sql)
39
peterm

SQL Server a une commande PIVOT qui pourrait correspondre à ce que vous recherchez.

select * from Tag
pivot (MAX(Value) for TagID in ([A1],[A2],[A3],[A4])) as TagTime;

Si les colonnes ne sont pas constantes, vous devrez combiner cela avec du SQL dynamique.

DECLARE @columns AS VARCHAR(MAX);
DECLARE @sql AS VARCHAR(MAX);

select @columns = substring((Select DISTINCT ',' + QUOTENAME(TagID) FROM Tag FOR XML PATH ('')),2, 1000);

SELECT @sql =

'SELECT *
FROM TAG
PIVOT 
(
  MAX(Value) 
  FOR TagID IN( ' + @columns + ' )) as TagTime;';

 execute(@sql);
10
sarme

Une autre option qui pourrait convenir dans cette situation consiste à utiliser XML.

L'option XML permettant de transposer des lignes en colonnes est fondamentalement une version optimale du PIVOT dans la mesure où elle aborde la limitation dynamique des colonnes.

La version XML du script corrige cette limitation en combinant chemin XML, T-SQL dynamique et certaines fonctions intégrées (c'est-à-dire STUFF, QUOTENAME).

Expansion verticale

Semblables au PIVOT et au Curseur, les nouvelles politiques ajoutées peuvent être récupérées dans la version XML du script sans modifier le script d'origine.

Expansion horizontale

Contrairement au PIVOT, les documents récemment ajoutés peuvent être affichés sans modifier le script.

Ventilation des performances

En termes d'E/S, les statistiques de la version XML du script sont presque similaires à celles du PIVOT. La seule différence est que le code XML comporte une seconde analyse de la table dtTranspose, mais cette fois à partir d'un cache logique de lecture de données.

Vous pouvez trouver plus d'informations sur ces solutions (y compris quelques exemples réels de T-SQL) dans cet article: https://www.sqlshack.com/multiple-options-to-transposing-rows-into-columns/

10
FloresP

Basé sur la solution de bluefeet , voici une procédure stockée qui utilise SQL dynamique pour générer la table transposée. Cela nécessite que tous les champs soient numériques, à l'exception de la colonne transposée (la colonne qui sera l'en-tête dans la table résultante):

/****** Object:  StoredProcedure [dbo].[SQLTranspose]    Script Date: 11/10/2015 7:08:02 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:      Paco Zarate
-- Create date: 2015-11-10
-- Description: SQLTranspose dynamically changes a table to show rows as headers. It needs that all the values are numeric except for the field using for     transposing.
-- Parameters: @TableName - Table to transpose
--             @FieldNameTranspose - Column that will be the new headers
-- Usage: exec SQLTranspose <table>, <FieldToTranspose>
--        table and FIeldToTranspose should be written using single quotes
-- =============================================
ALTER PROCEDURE [dbo].[SQLTranspose] 
  -- Add the parameters for the stored procedure here
  @TableName NVarchar(MAX) = '', 
  @FieldNameTranspose NVarchar(MAX) = ''
AS
BEGIN
  -- SET NOCOUNT ON added to prevent extra result sets from
  -- interfering with SELECT statements.
  SET NOCOUNT ON;

  DECLARE @colsUnpivot AS NVARCHAR(MAX),
  @query  AS NVARCHAR(MAX),
  @queryPivot  AS NVARCHAR(MAX),
  @colsPivot as  NVARCHAR(MAX),
  @columnToPivot as NVARCHAR(MAX),
  @tableToPivot as NVARCHAR(MAX), 
  @colsResult as xml

  select @tableToPivot = @TableName;
  select @columnToPivot = @FieldNameTranspose


  select @colsUnpivot = stuff((select ','+quotename(C.name)
       from sys.columns as C
       where C.object_id = object_id(@tableToPivot) and
             C.name <> @columnToPivot 
       for xml path('')), 1, 1, '')

  set @queryPivot = 'SELECT @colsResult = (SELECT  '','' 
                    + quotename('+@columnToPivot+')
                  from '+@tableToPivot+' t
                  where '+@columnToPivot+' <> ''''
          FOR XML PATH(''''), TYPE)'

  exec sp_executesql @queryPivot, N'@colsResult xml out', @colsResult out

  select @colsPivot = STUFF(@colsResult.value('.', 'NVARCHAR(MAX)'),1,1,'')

  set @query 
    = 'select name, rowid, '+@colsPivot+'
        from
        (
          select '+@columnToPivot+' , name, value, ROW_NUMBER() over (partition by '+@columnToPivot+' order by '+@columnToPivot+') as rowid
          from '+@tableToPivot+'
          unpivot
          (
            value for name in ('+@colsUnpivot+')
          ) unpiv
        ) src
        pivot
        (
          sum(value)
          for '+@columnToPivot+' in ('+@colsPivot+')
        ) piv
        order by rowid'
  exec(@query)
END
2
Paco Zarate

J'avais une exigence légèrement différente, en vertu de laquelle je devais transposer sélectivement des colonnes en lignes.

La table avait des colonnes:

create table tbl (ID, PreviousX, PreviousY, CurrentX, CurrentY)

J'avais besoin de colonnes pour Previous et Current, et de lignes pour X et Y. Un produit cartésien généré sur une table statique a bien fonctionné, par exemple:

select 
    ID,
    max(case when metric='X' then PreviousX
        case when metric='Y' then PreviousY end) as Previous,
    max(case when metric='X' then CurrentX
        case when metric='Y' then CurrentY end) as Current
from tbl inner join
    /* Cartesian product - transpose by repeating row and 
    picking appropriate metric column for period */
    ( VALUES (1, 'X'), (2, 'Y')) AS x (sort, metric) ON 1=1
group by ID
order by ID, sort
1
Chris