标题可能有点迟钝,我很难简洁地解释这一点。
我的数据看起来像这样( SourceTable ):
Date Type High Low
2017-01-10 cats 21.4 10.4
2017-01-10 dogs 20.4 8.4
2017-01-20 cats 40.1 21.2
2017-01-20 dogs 20.1 1.0
我有大约300种不同的"类型" (如在“类型”列中)。该类型是指向所有类型的表的外键。
类型表
Type
cats
dogs
并非所有类型都在每个日期都有代表。
我想让上表看起来像这样( FlippedTable ):
Date Cats_High Cats_Low Dogs_High Dogs_Low
2017-01-10 21.4 10.4 20.4 8.4
2017-01-20 40.1 21.2 20.1 1.0
我有一个工作解决方案,它在动态SQL中使用WHILE循环,通过不断更改它来添加新列并插入新数据来构建临时表。它有效,但执行需要花费大量时间。我觉得我的动态SQL方法效率低下,可能会用一两个轴来改进。
当然,我们可以争辩说,首先这样做是低效和愚蠢的,但不幸的是我有理由。
这是我的动态SQL方法:
DECLARE @type_counter INT = 0;
DECLARE @date_counter INT = 0;
DECLARE @all_types_total INT;
DECLARE @all_dates_total INT;
DECLARE @current_date DATE;
DECLARE @current_type NVARCHAR(80);
DECLARE @dynamic_sql NVARCHAR(max);
CREATE TABLE #FlippedTable([Date] DATE PRIMARY KEY);
SELECT @all_types_total=COUNT(*) FROM types;
WHILE @type_counter < @all_types_total
BEGIN
SELECT @current_type=type FROM (
SELECT
type,
ROW_NUMBER() OVER(ORDER BY type) as [RowNumber]
FROM SourceTable)
AS subquery WHERE [RowNumber] = @type_counter;
SET @dynamic_sql = 'ALTER TABLE #FlippedTable ADD [' + @current_type + '_Low] NUMERIC(7,2) NULL, ['+@current_type+'_High] NUMERIC(7,2)'
EXECUTE sp_executeSQL @dynamic_sql;
SELECT @all_dates_total = COUNT(*) FROM SourceTable WHERE type = @current_type;
WHILE @date_counter < @all_dates_total
BEGIN
SELECT @current_date = [Date] FROM (SELECT [Date], ROW_NUMBER() OVER(ORDER BY [Date]) AS [RowNumber] FROM SourceTable) AS subquery WHERE [RowNumber] = @date_counter;
IF EXISTS(SELECT * FROM FlippedTable WHERE [Date] = @current_date)
BEGIN
SET @dynamic_sql = 'UPDATE FlippedTable SET [' + @current_type + '_Low] = tb2.[Low], ['+@current_type+'_High] = tb2.[High] FROM FlippedTable AS tb1 INNER JOIN SourceTable AS tb2 ON tb1.Date = tb2.Date WHERE tb2.type = ''' + @current_type + ''' AND tb2.Date = ''' + CAST(@current_date AS nvarchar(50)) + '''';
EXECUTE sp_executeSQL @dynamic_sql;
END
ELSE
BEGIN
SET @dynamic_sql = 'INSERT INTO FlippedTable ([Date], ['+@current_type+'_Low], ['+@current_type+'_High] FROM SourceTable WHERE type = ''' + @current_type + ''' AND SourceTable.Date = ''' + CAST(@current_date AS nvarchar(50)) + '''';
END
SET @date_counter = @date_counter + 1;
END;
SET @date_counter = 0;
SET @type_counter = @type_counter + 1;
END;
添加&#34;类型&#34;表非常罕见。我知道所有类型,所以如果必须的话,我可以对所有可能的列名进行硬编码,尽管它不理想。
我希望有一个PIVOT解决方案可以解决这个问题。在这种情况下,我还没有能够理解如何使用该工具。无论更好的解决方案是什么,我觉得它使用的是我目前不熟悉的工具。
答案 0 :(得分:1)
应尽可能避免循环。您可以通过一些动态SQL
获得所需的结果示例强>
Declare @SQL varchar(max) = '
Select *
From (
Select A.Date
,B.*
From YourTable A
Cross Apply ( values (Type+''_High'',High)
,(Type+''_Low'',Low)
) B(Item,Value)
) A
Pivot (max([Value]) For [Item] in (' + Stuff((Select Distinct ','+QuoteName(Type+'_High')
+','+QuoteName(Type+'_Low')
From (Select Distinct Type From YourTable) A
Order By 1
For XML Path('')),1,1,'') + ') ) p'
Exec(@SQL);
--Print @SQL
<强>返回强>
Date cats_High cats_Low dogs_High dogs_Low
2017-01-10 21.40 10.40 20.40 8.40
2017-01-20 40.10 21.20 20.10 1.00