web-dev-qa-db-fra.com

Est-il possible d'alias une colonne basée sur le résultat d'un select + où?

J'ai une table comme celle-ci:

TB1:

COD | A001 | A002 | A003
 1    cars   baby   nasa

puis un deuxième tableau:

TB2:

COD | NO_COL_TB1 | DESCRIPTION |
 1  |    A001    |  Something
 2  |    A002    |   lasagna

Ce que j'essaye de faire est quelque chose comme ça (évidemment ça ne marche pas)

select A001 as (select description from TB2 WHERE no_col_tb1= A001 )
      ,A002 AS (select description from TB2 WHERE no_col_tb1= A002 )
from TB1

j'ai essayé avec des jointures internes et un sql dynamique mais je ne peux pas penser à une logique pour cela. Il y a des questions à ce sujet, mais aucun d'eux ne pourrait m'aider. Je pense que cela est impossible à faire avec une seule déclaration et uniquement possible avec un SQL dynamique.

ÉDITER:

Le résultat correct serait:

SELECT COD,
       TB1.A001 AS 'Something',
       TB1.A002 AS 'Lasagna'
FROM TB1

et le résultat serait:

COD |SOMETHING | LASAGNA |
 1      cars       baby
6
Racer SQL

Désolé de le dire, mais la structure de votre table est difficile à travailler compte tenu de ce que vous voulez faire. Il y a plusieurs façons d'obtenir le résultat, une façon serait d'utiliser UNPIVOT et PIVOT, mais c'est moche.

Vous pouvez commencer par UNPIVOT ing les données dans TB1 de vos colonnes aux lignes:

select *
from tb1 t1
unpivot 
(
    val
    for col in (A001, A002, A003)
) u

Cela va retourner les données au format:

| COD |  val |  col |
|-----|------|------|
|   1 | cars | A001 |
|   1 | baby | A002 |
|   1 | nasa | A003 |

Ensuite, vous pouvez prendre ce résultat et le joindre à TB2:

select 
    d.COD,
    d.val,
    t2.DESCRIPTION
from
(
    select *
    from tb1 t1
    unpivot 
    (
        val
        for col in (A001, A002, A003)
    ) u
)d
inner join tb2 t2
    on d.col = t2.NO_COL_TB1;

Ce qui donne le résultat:

| COD |  val | DESCRIPTION |
|-----|------|-------------|
|   1 | cars |   Something |
|   1 | baby |     lasagna |

Vous avez maintenant vos nouveaux noms de colonne dans les lignes Description et val, mais vous les voulez dans les colonnes, vous pouvez donc maintenant lui appliquer la fonction PIVOT:

select *
from
(
    select 
        d.COD,
        d.val,
        t2.DESCRIPTION
    from
    (
        select *
        from tb1 t1
        unpivot 
        (
            val
            for col in (A001, A002, A003)
        ) u
    )d
    inner join tb2 t2
        on d.col = t2.NO_COL_TB1
) x
pivot
(
    max(val)
    for description in (Something, Lasagna)
) p;

Cela génère le résultat final que vous souhaitez:

| COD | Something | Lasagna |
|-----|-----------|---------|
|   1 |      cars |    baby |

Maintenant, tout cela est super si vous connaissez toutes les colonnes dont vous avez besoin pour UNPIVOT puis PIVOT, mais si vous ne le faites pas, vous devrez utiliser du SQL dynamique pour le résoudre. Qui ressemblera à ceci:

DECLARE 
    @colsUnpivot AS NVARCHAR(MAX),
    @colsPivot 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 = 'tb1' and
                 C.column_name like 'A%'
           for xml path('')), 1, 1, '')

select @colsPivot 
  = stuff((select ','+quotename([DESCRIPTION])
           from Tb2
           for xml path('')), 1, 1, '')


set @query = 'select *
            from
            (
                select 
                    d.COD,
                    d.val,
                    t2.DESCRIPTION
                from
                (
                    select *
                    from tb1 t1
                    unpivot 
                    (
                        val
                        for col in ('+ @colsUnpivot +')
                    ) u
                )d
                inner join tb2 t2
                    on d.col = t2.NO_COL_TB1
            ) x
            pivot
            (
                max(val)
                for description in ('+@colsPivot+')
            ) p;';

exec sp_executesql @query;

C'est long, mais ça devrait vous donner le même résultat. ( démo dbfiddle )

25
Taryn
DECLARE @SQL NVARCHAR(MAX)

SELECT @SQL = STUFF(
    ( SELECT ', ' + QUOTENAME(C.COLUMN_NAME) + ' AS ' + QUOTENAME(F.DESCRICAO)
FROM INFORMATION_SCHEMA.COLUMNS C 
    INNER JOIN TABBASE F ON C.COLUMN_NAME = F.NO_COL_TABBASE
WHERE C.TABLE_NAME = 'TABELA_ENTRADA'
ORDER BY C.ORDINAL_POSITION FOR XML PATH('')),1,2,'')

SELECT @SQL = 'SELECT ' + @SQL + ' FROM tabela_entrada'

PRINT @SQL

EXECUTE(@SQL)

Y a-t-il une meilleure façon que cela? Je ne savais pas que je pouvais utiliser STUFF pour cela. J'ai trouvé cette requête sur cette page Microsoft .

4
Racer SQL

Je l'ai fait lors de la création de données dynamiques pour un publipostage Word. Les noms des colonnes doivent être connus au moment de la compilation, vous devrez donc utiliser SQL dynamique.

Lorsque vous avez besoin de beaucoup de colonnes ou de colonnes de grandes tailles, vous atteindrez la limite de taille de ligne pour la fonction PIVOT. Dans ce cas, vous devrez effectuer le pivot à l'aide de GROUP BY.

DECLARE @Pivot table ( [RowID] int, [ColumnName] nvarchar(50), [ColumnValue] nvarchar(50) );

INSERT INTO @Pivot VALUES (1,'Something','cars'), (1,'lasagna','baby'), (2,'Something','other'), (2,'lasagna','data');

SELECT 
    [RowID],
    [Something] = MAX(CASE WHEN [ColumnName] = 'Something' THEN [ColumnValue] END),
    [lasagna] = MAX(CASE WHEN [ColumnName] = 'lasagna' THEN [ColumnValue] END)
FROM 
    @Pivot
GROUP BY 
    [RowID];

Votre structure de données vous oblige à débloquer pour rejoindre la table avec les noms de vos colonnes. Je préfère le faire en utilisant une application croisée.

DECLARE @TB1 table ( [COD] int, [A001] varchar(50), [A002] varchar(50), [A003] varchar(50) );
DECLARE @TB2 table ( [COD] int, [NO_COL_TB1] varchar(50), [DESCRIPTION] varchar(50) );

INSERT INTO @TB1 values (1,'cars','baby','nasa')
INSERT INTO @TB2 values (1,'A001','Something'), (2,'A002','lasagna')


CREATE TABLE #LargePivotData ( [RowID] int not null, [ColumnName] nvarchar(128) not null, [ColumnValue] nvarchar(max) ); 
-- QUOTENAME will return null for anything longer than 128


INSERT INTO 
    #LargePivotData
SELECT 
    [RowID] = [TB1].[COD],
    [ColumnName] = [DESCRIPTION],
    [ColumnValue] = [TB1_Value]
FROM 
    @TB1 AS [TB1]
    --unpivot
    CROSS APPLY (VALUES
        ('A001', [A001]),
        ('A002', [A002]),
        ('A003', [A003])
    ) AS [TB1_Unpivot]([TB1_Column], [TB1_Value])
    INNER JOIN @TB2 AS [TB2]
        ON [TB1_Unpivot].[TB1_Column] = [TB2].[NO_COL_TB1]


DECLARE @Columns nvarchar(max), @ColumnValues nvarchar(max);

WITH [ColumnNameValues] AS (
    SELECT DISTINCT
        [Name] = QUOTENAME([ColumnName]),
        [Value] = QUOTENAME([ColumnName]) + N' = MAX(CASE WHEN [ColumnName] = N' + QUOTENAME([ColumnName], '''') + N' THEN [ColumnValue] END)'
    FROM 
        #LargePivotData
)
SELECT 
    @Columns = COALESCE(@Columns + N',' + [Name], [Name]),
    @ColumnValues = COALESCE(@ColumnValues + N',' + [Value], [Value])
FROM 
    [ColumnNameValues]
ORDER BY
    [Name];

DECLARE @Sql nvarchar(max) = N'
    WITH [PivotedData] AS (
        SELECT
            [RowID],
            ' + @ColumnValues + N'
        FROM
            #LargePivotData
        GROUP BY
            [RowID]
    )
    SELECT 
        [RowID],
        ' + @Columns + N'
    FROM 
        [PivotedData]
        -- join on any additional tables
    ';

--PRINT @Sql;

DECLARE @params nvarchar(4000) = N''; -- for additional parameters used in @Sql

EXEC sp_executesql @Sql, @params;

DROP TABLE #LargePivotData;
0
Nicholas