简体   繁体   中英

Sql mapping with column name and first row values

Suppose I have a table:

C1   C2   C3   C4
--   --   --   --
v11  v12  v13  v14
v21  v22  v23  v24

I want the output to be ColumnName and First Row mapped, like below:

        NC1   NC2
        --    --  
        C1    v11 
        C2    v12 
        C3    v13
        C4    v14

What will be the SQL query for this?

I need to do it dynamically - without knowing actually how much columns I have in the table!

Do a UNION ALL :

select 'C1' as nc1, c1 as nc2 from tablename where c1 = 'v11'
union all
select 'C2', c2 from tablename where c1 = 'v11'
union all
select 'C3', c3 from tablename where c1 = 'v11'
union all
select 'C4', c4 from tablename where c1 = 'v11'

Perhaps you need to add ORDER BY nc1 at the end.

Common table expression version:

witc cte as
(
    select * from tablename where c1 = 'v11'
)
select 'C1' as nc1, c1 as nc2 from cte
union all
select 'C2', c2 from cte
union all
select 'C3', c3 from cte
union all
select 'C4', c4 from cte

UPDATE for the dynamic version:

DECLARE @sql NVARCHAR(MAX) = '';

SELECT @sql = 
'SELECT
    x.NC1, x.NC2
FROM tbl t
CROSS APPLY ( VALUES ' +
(SELECT STUFF((
    SELECT ',(''' +COLUMN_NAME + ''', ' + QUOTENAME(COLUMN_NAME) + ')'
    FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'Tbl'            
    FOR XML PATH('')
), 1, 1, '')) +
') x (NC1, NC2)
WHERE
    t.C1 = ''V11''';

PRINT (@sql);
EXEC (@sql);

Another method is to use CROSS APPLY . This is more efficient since it only scans the table once:

SELECT 
    x.NC1, x.NC2
FROM tbl t
CROSS APPLY ( VALUES
    ('C1', C1),
    ('C2', C2),
    ('C3', C3),
    ('C4', C4)
) x(NC1, NC2)
WHERE
    t.C1 = 'V11'

Reference: An Alternative (Better?) Method to UNPIVOT (SQL Spackle) - SQLServerCentral

Depending on context, I would use one of bellow solutions:

-- Source Table
IF EXISTS (SELECT * FROM sys.tables t WHERE t.schema_id = 1 AND t.name = N'SourceTable_83648326')
BEGIN
    DROP TABLE dbo.SourceTable_83648326
END

SELECT  *
INTO    dbo.SourceTable_83648326
FROM (VALUES 
    ('v11', 'v12', 'v13', 'v14'),
    ('v21', 'v22', 'v23', 'v24')
) SourceTable(C1, C2, C3, C4)
WHERE C1 = 'v11' -- It select required source row

-- Solution #1: dynamic query
DECLARE @Columns NVARCHAR(MAX) = N''
SELECT  @Columns = @Columns + N', ' + QUOTENAME(c.name)
FROM    sys.tables t JOIN sys.columns c ON t.object_id = c.object_id
WHERE   t.schema_id = 1 -- dbo
AND     t.name = N'SourceTable_83648326' -- Don't use ORDER BY or DISTINCT !

SET @Columns = STUFF(@Columns, 1, 2, '')

DECLARE @SqlStatement NVARCHAR(MAX) = N'
SELECT  up.*
FROM (
    SELECT  *
    FROM    dbo.SourceTable_83648326
    WHERE   C1 = @p_C1 -- It select required source row
) ups -- UnPivot source
UNPIVOT( NC2_Value FOR NC1_ColumnType IN (' + @Columns + ') ) up -- UnPivot'

EXEC sp_executesql @SqlStatement, N'@p_C1 VARCHAR(50)', @p_C1 = 'v11' -- Replace with propper data type and max length

.

-- Solution #2: static query
SELECT  
    b.XmlCol.value('local-name(.)', 'SYSNAME') AS NC1_ColumnType,
    b.XmlCol.value('.', 'VARCHAR(50)') AS NC2_Value -- Replace with propper data type and max length
FROM (
    VALUES((SELECT  *
    FROM    dbo.SourceTable_83648326
    WHERE   C1 = 'v11' -- It select required source row
    FOR XML RAW, TYPE))
) a(XmlCol)
CROSS APPLY a.XmlCol.nodes(N'row/@*') b(XmlCol)

Another way than UNION ALL & CROSS APPLY is to use UNPIVOT:

DECLARE @VarTable TABLE (C1 varchar(3), C2 varchar(3), C3 varchar(3), C4 varchar(3));

INSERT INTO @VarTable VALUES ('v11','v12','v13','v14');
INSERT INTO @VarTable VALUES ('v21','v22','v23','v24');

SELECT nc1, nc2
FROM (select * from @VarTable where c1 = 'v11') Q
UNPIVOT
(
nc2 for nc1 in (c1, c2, c3, c4)
) U
ORDER BY nc1, nc2;

But I actually prefere Solution #2 from Bogdan Sahlean that uses XML & CROSS APPLY.
Since that's a concise method that doesn't require the column names to be hardcoded in the query.
The query below was copied and modified from his answer, just to test it out with the table variable.

SELECT
X2.XmlCol.value('local-name(.)', 'varchar(8)') AS nc1,
X2.XmlCol.value('.', 'varchar(8)') AS nc2
FROM (
    SELECT * FROM @VarTable 
    WHERE C1 = 'v11'
    FOR XML RAW, TYPE
) X1(XmlCol)
CROSS APPLY X1.XmlCol.nodes(N'row/@*') X2(XmlCol);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM