具有不同日期的列到行
Columns to Rows with varying dates
请参阅下面的示例数据和所需的输出格式:
--SAMPLE TABLE
DECLARE @TEMP TABLE(
DATA_DATE DATE,
PROD_ID INT,
CAT_CODE NVARCHAR(10),
DATABUCKET_1 FLOAT,
DATABUCKET_2 FLOAT,
DATABUCKET_3 FLOAT,
DATABUCKET_4 FLOAT,
DATABUCKET_5 FLOAT);
INSERT INTO @TEMP VALUES('19-Oct-2018',100,'C1', 100,200,300,400,500)
SELECT * FROM @TEMP;
--PREFERRED OUTPUT FORMAT
SELECT 'C1' AS CAT_CODE, '19-Oct-2018' AS DATA_DATE, 100 AS UNITS, 'W1' AS WEEK_NUM--FOR DATABUCKET_1, THE DATE REMAINS SAME (AS DATA_DATE)
UNION ALL
SELECT 'C1' AS CAT_CODE, '12-Oct-2018' AS DATA_DATE, 200 AS UNITS, 'W2' AS WEEK_NUM--FOR DATABUCKET_2, THE DATE IS ONE WEEK BEFORE THAT OF W1
UNION ALL
SELECT 'C1' AS CAT_CODE, '05-Oct-2018' AS DATA_DATE, 300 AS UNITS, 'W3' AS WEEK_NUM--FOR DATABUCKET_3, THE DATE IS ONE WEEK BEFORE THAT OF W2
UNION ALL
SELECT 'C1' AS CAT_CODE, '28-Sep-2018' AS DATA_DATE, 400 AS UNITS, 'W4' AS WEEK_NUM--FOR DATABUCKET_4, THE DATE IS ONE WEEK BEFORE THAT OF W3
UNION ALL
SELECT 'C1' AS CAT_CODE, '21-Sep-2018' AS DATA_DATE, 500 AS UNITS, 'W5' AS WEEK_NUM--FOR DATABUCKET_5, THE DATE IS ONE WEEK BEFORE THAT OF W4
补充几点:
- 我的实际 table 有 106 个数据桶和其他几个列。
- 为了简单起见,我在这里只给出了几个。
- 每个月都会收到一个具有不同 DATA_DATE 值的新文件。
- DATA_DATE 值在一个文件中相同,对应于 DATABUCKET_1
- 对于其他DATABUCKETS,该值为一周前。
请告诉我如何使用 UNPIVOT 实现此目的。提前致谢
您可以使用 CROSS APPLY
执行 un-pivot
SELECT t.CAT_CODE, d.*
FROM @TEMP t
CROSS APPLY
(
SELECT DATA_DATE = t.DATA_DATE, UNITS = t.DATABUCKET_1, WEEK_NUM = 'W1' union all
SELECT DATA_DATE = DATEADD(DAY, -7, t.DATA_DATE), UNITS = t.DATABUCKET_2, WEEK_NUM = 'W2' union all
SELECT DATA_DATE = DATEADD(DAY, -14, t.DATA_DATE), UNITS = t.DATABUCKET_3, WEEK_NUM = 'W3' union all
SELECT DATA_DATE = DATEADD(DAY, -21, t.DATA_DATE), UNITS = t.DATABUCKET_4, WEEK_NUM = 'W4' union all
SELECT DATA_DATE = DATEADD(DAY, -28, t.DATA_DATE), UNITS = t.DATABUCKET_5, WEEK_NUM = 'W5'
) d
或使用计数/数字table
SELECT t.CAT_CODE, DATA_DATE = DATEADD(DAY, -7 * n, t.DATA_DATE),
UNITS = CASE n
WHEN 0 THEN t.DATABUCKET_1
WHEN 1 THEN t.DATABUCKET_2
WHEN 2 THEN t.DATABUCKET_3
WHEN 3 THEN t.DATABUCKET_4
WHEN 4 THEN t.DATABUCKET_5
END,
WEEK_NUM = 'W' + CONVERT(VARCHAR(10), n + 1)
FROM @TEMP t
INNER JOIN NUMBERS n ON n between 0 and 4
如果你真的有 106 个桶,你真的应该考虑标准化你的 table。否则您需要对 106 行重复上述操作。另一种方法是使用Dynamic SQL
来处理
这可以使用动态 T-SQL 语句轻松完成。我们的想法是提前获取我们需要用于反透视的列,并为每列添加订单 ID。该数字将用于计算最后一列和日期列。
请注意,我已将 @table variable
更改为 normal table
,以便能够从 sys.columns
视图中动态读取列。当然,在您的真实示例中,您可以随意填充 table 并根据需要对列进行排序。
--DROP TABLE IF EXISTS [dbo].[Temp];
CREATE TABLE [dbo].[Temp](
DATA_DATE DATE,
PROD_ID INT,
CAT_CODE NVARCHAR(10),
DATABUCKET_1 FLOAT,
DATABUCKET_2 FLOAT,
DATABUCKET_3 FLOAT,
DATABUCKET_4 FLOAT,
DATABUCKET_5 FLOAT);
INSERT INTO [dbo].[Temp] VALUES ('19-Oct-2018',100,'C1', 100,200,300,400,500)
SELECT * FROM [dbo].[Temp];
--PREFERRED OUTPUT FORMAT
SELECT 'C1' AS CAT_CODE, '19-Oct-2018' AS DATA_DATE, 100 AS UNITS, 'W1' AS WEEK_NUM--FOR DATABUCKET_1, THE DATE REMAINS SAME (AS DATA_DATE)
UNION ALL
SELECT 'C1' AS CAT_CODE, '12-Oct-2018' AS DATA_DATE, 200 AS UNITS, 'W2' AS WEEK_NUM--FOR DATABUCKET_2, THE DATE IS ONE WEEK BEFORE THAT OF W1
UNION ALL
SELECT 'C1' AS CAT_CODE, '05-Oct-2018' AS DATA_DATE, 300 AS UNITS, 'W3' AS WEEK_NUM--FOR DATABUCKET_3, THE DATE IS ONE WEEK BEFORE THAT OF W2
UNION ALL
SELECT 'C1' AS CAT_CODE, '28-Sep-2018' AS DATA_DATE, 400 AS UNITS, 'W4' AS WEEK_NUM--FOR DATABUCKET_4, THE DATE IS ONE WEEK BEFORE THAT OF W3
UNION ALL
SELECT 'C1' AS CAT_CODE, '21-Sep-2018' AS DATA_DATE, 500 AS UNITS, 'W5' AS WEEK_NUM--FOR DATABUCKET_5, THE DATE IS ONE WEEK BEFORE THAT OF W4
DECLARE @DynamicTSQLStatement NVARCHAR(MAX)
,@ColumnNames NVARCHAR(MAX);
--DROP TABLE IF EXISTS #Columns;
CREATE TABLE #Columns
(
[ID] INT
,[name] SYSNAME
);
INSERT INTO #Columns ([ID], [name])
SELECT ROW_NUMBER() OVER (ORDER BY [column_id]) - 1
,[name]
FROM [sys].[columns]
WHERE [object_id] = OBJECT_ID('[dbo].[Temp]')
AND [name] LIKE '%DATABUCKET%';
SELECT @ColumnNames = STUFF
(
(
SELECT ',[' + [name] + ']'
FROM #Columns
ORDER BY [id]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1
,1
,''
);
SET @DynamicTSQLStatement = N'
SELECT [CAT_CODE]
,DATEADD(WEEK, -1 * C.id, DATA_DATE) AS DATA_DATE
,value as UNITS
,''W'' + CAST(c.id + 1 AS VARCHAR(8)) as [WEEK_NUM]
FROM [dbo].[Temp]
UNPIVOT
(
[value] FOR [column] IN ('+ @ColumnNames +')
) UNPVT
INNER JOIN #Columns C
ON UNPVT.[column] = c.[name]
ORDER BY DATA_DATE DESC
;'
EXEC sp_executesql @DynamicTSQLStatement;
所以,这就是想法。操作代码以处理您的数据。
请参阅下面的示例数据和所需的输出格式:
--SAMPLE TABLE
DECLARE @TEMP TABLE(
DATA_DATE DATE,
PROD_ID INT,
CAT_CODE NVARCHAR(10),
DATABUCKET_1 FLOAT,
DATABUCKET_2 FLOAT,
DATABUCKET_3 FLOAT,
DATABUCKET_4 FLOAT,
DATABUCKET_5 FLOAT);
INSERT INTO @TEMP VALUES('19-Oct-2018',100,'C1', 100,200,300,400,500)
SELECT * FROM @TEMP;
--PREFERRED OUTPUT FORMAT
SELECT 'C1' AS CAT_CODE, '19-Oct-2018' AS DATA_DATE, 100 AS UNITS, 'W1' AS WEEK_NUM--FOR DATABUCKET_1, THE DATE REMAINS SAME (AS DATA_DATE)
UNION ALL
SELECT 'C1' AS CAT_CODE, '12-Oct-2018' AS DATA_DATE, 200 AS UNITS, 'W2' AS WEEK_NUM--FOR DATABUCKET_2, THE DATE IS ONE WEEK BEFORE THAT OF W1
UNION ALL
SELECT 'C1' AS CAT_CODE, '05-Oct-2018' AS DATA_DATE, 300 AS UNITS, 'W3' AS WEEK_NUM--FOR DATABUCKET_3, THE DATE IS ONE WEEK BEFORE THAT OF W2
UNION ALL
SELECT 'C1' AS CAT_CODE, '28-Sep-2018' AS DATA_DATE, 400 AS UNITS, 'W4' AS WEEK_NUM--FOR DATABUCKET_4, THE DATE IS ONE WEEK BEFORE THAT OF W3
UNION ALL
SELECT 'C1' AS CAT_CODE, '21-Sep-2018' AS DATA_DATE, 500 AS UNITS, 'W5' AS WEEK_NUM--FOR DATABUCKET_5, THE DATE IS ONE WEEK BEFORE THAT OF W4
补充几点:
- 我的实际 table 有 106 个数据桶和其他几个列。
- 为了简单起见,我在这里只给出了几个。
- 每个月都会收到一个具有不同 DATA_DATE 值的新文件。
- DATA_DATE 值在一个文件中相同,对应于 DATABUCKET_1
- 对于其他DATABUCKETS,该值为一周前。
请告诉我如何使用 UNPIVOT 实现此目的。提前致谢
您可以使用 CROSS APPLY
un-pivot
SELECT t.CAT_CODE, d.*
FROM @TEMP t
CROSS APPLY
(
SELECT DATA_DATE = t.DATA_DATE, UNITS = t.DATABUCKET_1, WEEK_NUM = 'W1' union all
SELECT DATA_DATE = DATEADD(DAY, -7, t.DATA_DATE), UNITS = t.DATABUCKET_2, WEEK_NUM = 'W2' union all
SELECT DATA_DATE = DATEADD(DAY, -14, t.DATA_DATE), UNITS = t.DATABUCKET_3, WEEK_NUM = 'W3' union all
SELECT DATA_DATE = DATEADD(DAY, -21, t.DATA_DATE), UNITS = t.DATABUCKET_4, WEEK_NUM = 'W4' union all
SELECT DATA_DATE = DATEADD(DAY, -28, t.DATA_DATE), UNITS = t.DATABUCKET_5, WEEK_NUM = 'W5'
) d
或使用计数/数字table
SELECT t.CAT_CODE, DATA_DATE = DATEADD(DAY, -7 * n, t.DATA_DATE),
UNITS = CASE n
WHEN 0 THEN t.DATABUCKET_1
WHEN 1 THEN t.DATABUCKET_2
WHEN 2 THEN t.DATABUCKET_3
WHEN 3 THEN t.DATABUCKET_4
WHEN 4 THEN t.DATABUCKET_5
END,
WEEK_NUM = 'W' + CONVERT(VARCHAR(10), n + 1)
FROM @TEMP t
INNER JOIN NUMBERS n ON n between 0 and 4
如果你真的有 106 个桶,你真的应该考虑标准化你的 table。否则您需要对 106 行重复上述操作。另一种方法是使用Dynamic SQL
来处理
这可以使用动态 T-SQL 语句轻松完成。我们的想法是提前获取我们需要用于反透视的列,并为每列添加订单 ID。该数字将用于计算最后一列和日期列。
请注意,我已将 @table variable
更改为 normal table
,以便能够从 sys.columns
视图中动态读取列。当然,在您的真实示例中,您可以随意填充 table 并根据需要对列进行排序。
--DROP TABLE IF EXISTS [dbo].[Temp];
CREATE TABLE [dbo].[Temp](
DATA_DATE DATE,
PROD_ID INT,
CAT_CODE NVARCHAR(10),
DATABUCKET_1 FLOAT,
DATABUCKET_2 FLOAT,
DATABUCKET_3 FLOAT,
DATABUCKET_4 FLOAT,
DATABUCKET_5 FLOAT);
INSERT INTO [dbo].[Temp] VALUES ('19-Oct-2018',100,'C1', 100,200,300,400,500)
SELECT * FROM [dbo].[Temp];
--PREFERRED OUTPUT FORMAT
SELECT 'C1' AS CAT_CODE, '19-Oct-2018' AS DATA_DATE, 100 AS UNITS, 'W1' AS WEEK_NUM--FOR DATABUCKET_1, THE DATE REMAINS SAME (AS DATA_DATE)
UNION ALL
SELECT 'C1' AS CAT_CODE, '12-Oct-2018' AS DATA_DATE, 200 AS UNITS, 'W2' AS WEEK_NUM--FOR DATABUCKET_2, THE DATE IS ONE WEEK BEFORE THAT OF W1
UNION ALL
SELECT 'C1' AS CAT_CODE, '05-Oct-2018' AS DATA_DATE, 300 AS UNITS, 'W3' AS WEEK_NUM--FOR DATABUCKET_3, THE DATE IS ONE WEEK BEFORE THAT OF W2
UNION ALL
SELECT 'C1' AS CAT_CODE, '28-Sep-2018' AS DATA_DATE, 400 AS UNITS, 'W4' AS WEEK_NUM--FOR DATABUCKET_4, THE DATE IS ONE WEEK BEFORE THAT OF W3
UNION ALL
SELECT 'C1' AS CAT_CODE, '21-Sep-2018' AS DATA_DATE, 500 AS UNITS, 'W5' AS WEEK_NUM--FOR DATABUCKET_5, THE DATE IS ONE WEEK BEFORE THAT OF W4
DECLARE @DynamicTSQLStatement NVARCHAR(MAX)
,@ColumnNames NVARCHAR(MAX);
--DROP TABLE IF EXISTS #Columns;
CREATE TABLE #Columns
(
[ID] INT
,[name] SYSNAME
);
INSERT INTO #Columns ([ID], [name])
SELECT ROW_NUMBER() OVER (ORDER BY [column_id]) - 1
,[name]
FROM [sys].[columns]
WHERE [object_id] = OBJECT_ID('[dbo].[Temp]')
AND [name] LIKE '%DATABUCKET%';
SELECT @ColumnNames = STUFF
(
(
SELECT ',[' + [name] + ']'
FROM #Columns
ORDER BY [id]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1
,1
,''
);
SET @DynamicTSQLStatement = N'
SELECT [CAT_CODE]
,DATEADD(WEEK, -1 * C.id, DATA_DATE) AS DATA_DATE
,value as UNITS
,''W'' + CAST(c.id + 1 AS VARCHAR(8)) as [WEEK_NUM]
FROM [dbo].[Temp]
UNPIVOT
(
[value] FOR [column] IN ('+ @ColumnNames +')
) UNPVT
INNER JOIN #Columns C
ON UNPVT.[column] = c.[name]
ORDER BY DATA_DATE DESC
;'
EXEC sp_executesql @DynamicTSQLStatement;
所以,这就是想法。操作代码以处理您的数据。