You could use CROSS APPLY
:
DECLARE @ngkbm_template_data_sets_ TABLE(
id INT IDENTITY(1,1) PRIMARY KEY,
txt_description_1 VARCHAR(50) NULL, -- or NVARCHAR,etc.
txt_description_2 VARCHAR(50) NULL,
txt_description_3 VARCHAR(50) NULL,
txt_description_4 VARCHAR(50) NULL
);
INSERT @ngkbm_template_data_sets_(txt_description_1,txt_description_2,txt_description_3,txt_description_4)
VALUES ('Protocol','Test','Dx',NULL);
SELECT x.id,y.*
FROM @ngkbm_template_data_sets_ x
CROSS APPLY(
SELECT x.txt_description_1 AS Value, 'txt_description_1' AS ColumnName
UNION ALL
SELECT x.txt_description_2 AS Value, 'txt_description_2' AS ColumnName
UNION ALL
SELECT x.txt_description_3 AS Value, 'txt_description_3' AS ColumnName
UNION ALL
SELECT x.txt_description_4 AS Value, 'txt_description_4' AS ColumnName
)y;
Results:
id Value ColumnName
-- -------- -----------------
1 Protocol txt_description_1
1 Test txt_description_2
1 Dx txt_description_3
1 NULL txt_description_4
Edit 1:
In following test, both solutions (CROSS APPLY + UNION ALL
, UNION ALL only
) have comparable performance from the point of view of CPU time
and elapsed time
CPU time = 156 ms, elapsed time = 2452 ms.
vs.
CPU time = 172 ms, elapsed time = 2344 ms.
but from the point of view of LIO
(logical reads
) the results show a huge difference:
Table 'SalesOrderHeader'. Scan count 1, logical reads 686
vs.
Table 'SalesOrderHeader'. Scan count 6, logical reads 2881.
Actual execution plan for every solutions show the cause of this difference:
The CROSS APPLY + UNION ALL
solution uses only one Clustered Index Scan
but UNION ALL only
solution uses four Clustered Index Scan
operators and two Index Scan
operators. Also, the estimated overall cost of every query is shows that SQL Servers "thinks" that CROSS APPLY + UNION ALL
(39%) solution is a little bit better than UNION ALL only
(61%). For this test I used AdventureWorks2008 R2 database.
SET NOCOUNT ON;
SET STATISTICS IO,TIME ON;
GO
PRINT 'CROSS APPLY';
SELECT h.SalesOrderID,x.*
FROM Sales.SalesOrderHeader h
CROSS APPLY(
SELECT h.SalesOrderNumber AS Value, 'SalesOrderNumber' AS RowType
UNION ALL
SELECT h.PurchaseOrderNumber, 'PurchaseOrderNumber'
UNION ALL
SELECT h.AccountNumber , 'AccountNumber'
UNION ALL
SELECT h.CreditCardApprovalCode , 'CreditCardApprovalCode'
UNION ALL
SELECT h.Comment , 'Comment'
UNION ALL
SELECT CONVERT(VARCHAR(36),h.rowguid) , 'rowguid'
)x
GO
PRINT 'UNION ALL only';
SELECT h.SalesOrderID,h.SalesOrderNumber AS Value, 'SalesOrderNumber' AS RowType FROM Sales.SalesOrderHeader h
UNION ALL
SELECT h.SalesOrderID,h.PurchaseOrderNumber, 'PurchaseOrderNumber' FROM Sales.SalesOrderHeader h
UNION ALL
SELECT h.SalesOrderID,h.AccountNumber , 'AccountNumber' FROM Sales.SalesOrderHeader h
UNION ALL
SELECT h.SalesOrderID,h.CreditCardApprovalCode , 'CreditCardApprovalCode' FROM Sales.SalesOrderHeader h
UNION ALL
SELECT h.SalesOrderID,h.Comment , 'Comment' FROM Sales.SalesOrderHeader h
UNION ALL
SELECT h.SalesOrderID,CONVERT(VARCHAR(36),h.rowguid) , 'rowguid' FROM Sales.SalesOrderHeader h
GO
SET NOCOUNT OFF;
SET STATISTICS IO,TIME OFF;
GO
/*
-- Output
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
CROSS APPLY + UNION ALL
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms.
Table 'SalesOrderHeader'. Scan count 1, logical reads 686, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 156 ms, elapsed time = 2452 ms.
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
UNION ALL only
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms.
Table 'SalesOrderHeader'. Scan count 6, logical reads 2881, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 172 ms, elapsed time = 2344 ms.
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms.
*/