具有列名和第一行值的SQL映射

时间:2016-05-27 07:26:05

标签: sql sql-server

假设我有一张桌子:

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

我希望输出为ColumnName和First Row映射,如下所示:

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

这个SQL查询是什么?

我需要动态地执行它 - 而不知道实际上我在表中有多少列!

4 个答案:

答案 0 :(得分:2)

执行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'

ORDER BY nc1

也许您需要在最后添加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

公用表表达式版本:

type

答案 1 :(得分:2)

动态版本的更新:

CROSS APPLY

另一种方法是使用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' 。这样效率更高,因为它只扫描一次表:

author_lit| literature_id|  title1      |      title2
-------------------------------------------------------
--------------------------------------------------------
thomas    |      1      |   xxxxxxx |  yyyyyyyy
------------------------------------------------------
-----------------------------------------------------------
Mr.xxx     |      2     |    xxxx            |  Yyyyy
-------------------------------------------------
---------------------------------------------

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

答案 2 :(得分:2)

根据具体情况,我会使用以下方法之一:

HKEY_CURRENT_USER\Control Panel\Colors

-- 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

答案 3 :(得分:0)

另一种方式比UNION ALL& CROSS APPLY是使用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;

但我实际上更喜欢Bogdan Sahlean的解决方案#2使用XML&交叉申请。
由于这是一个简洁的方法,不需要在查询中硬编码列名。
下面的查询是从他的回答中复制和修改的,只是为了用表变量测试它。

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);