繁体   English   中英

带有列名和第一行值的 Sql 映射

[英]Sql mapping with column name and first row values

假设我有一张桌子:

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 查询是什么?

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

做一个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'

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

动态版本的UPDATE:

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

另一种方法是使用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'

参考: UNPIVOT的替代方法(更好?)(SQL Spackle)-SQLServerCentral

根据上下文,我将使用以下解决方案之一:

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

除了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和CROSS APPLY。
由于这是一种简洁的方法,不需要在查询中对列名进行硬编码。
下面的查询是从他的答案中复制和修改的,只是为了使用表变量对其进行测试。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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