[英]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'
根據上下文,我將使用以下解決方案之一:
-- 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.