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