我正在尝试根据时间戳获取数据的数据透视。我想将它们分成半小时的“水桶”。例如,使用以下数据:
CREATE TABLE #test (
Employee nvarchar(20) NOT NULL
,[SaleTime] time NOT NULL
,Amount float NOT NULL
)
INSERT INTO #test VALUES
('A', '08:10', '100.50')
,('A', '12:20', '758.23')
,('A', '11:59', '592.11')
,('B', '12:00', '95.00')
,('B', '09:01', '29.10')
,('B', '09:04', '53.22')
,('C', '11:23', '55.77')
,('C', '10:40', '128.00')
我希望结果像是
Time | A | B | C |
-----------------------------------------------------------------
08:00 - 08:30 | 100.5 | | |
08:30 - 09:00 | | | |
09:00 - 09:30 | | 82.32 | |
09:30 - 10:00 | | | |
10:00 - 10:30 | | | |
10:30 - 11:00 | | | 128.00 |
11:00 - 11:30 | | | 55.77 |
11:30 - 12:00 | 592.11 | | |
12:00 - 12:30 | 758.23 | 95.00 | |
12:30 - 13:00 | | | |
-----------------------------------------------------------------
我是否必须创建一个带有时隙的空表才能执行此操作?有没有一种方法可以不使用CASE WHEN?
谢谢!
答案 0 :(得分:2)
也许我正在尝试解决一个不存在的问题,但我想提供一个替代解决方案,这个解决方案在员工数量方面是动态的(它为额外员工增加了列数)并总结了销售额。如果在该位置有多个销售,则每个员工的时间段;如果你没有汇总金额,那么对于某个人已售出多次的每个时段,你最终会有多行。
时间段生成借鉴了Nenads答案的优秀解决方案。
首先使用几行额外的行测试数据来说明差异:
DROP TABLE #test;
CREATE TABLE #test (
Employee nvarchar(20) NOT NULL
,[SaleTime] time NOT NULL
,Amount float NOT NULL
)
INSERT INTO #test VALUES
('A', '08:10', '100.50')
,('A', '12:20', '758.23')
,('A', '11:59', '592.11')
,('B', '12:00', '95.00')
,('B', '09:01', '29.10')
,('B', '09:04', '53.22')
,('C', '11:23', '55.77')
,('C', '10:40', '128.00')
,('D', '09:40', '28.00')
,('E', '11:40', '50.00')
,('E', '11:35', '20.00')
查询动态构建SQL语句并使用EXECUTE语句执行它:
DECLARE @Headers VARCHAR(MAX)
SELECT @Headers = COALESCE(@Headers + ',[' + Employee + ']', '[' + Employee + ']')
FROM (SELECT DISTINCT Employee FROM #test) Emp
DECLARE @SQL NVARCHAR(MAX)
SET @SQL = N'
WITH CTE_TimeSlots AS
(
SELECT CAST(''8:00'' AS TIME) AS StartTime, CAST(''8:30'' AS TIME) AS EndTime
UNION ALL
SELECT DATEADD(MI, 30, StartTime), DATEADD(MI, 30, EndTime)
FROM CTE_TimeSlots
WHERE StartTime <= ''12:00''
)
SELECT * FROM (
SELECT CONVERT(NVARCHAR(10),StartTime) + '' - '' + CONVERT(NVARCHAR(10),EndTime) AS [Time], Amount, Employee
FROM CTE_TimeSlots t
LEFT JOIN #test d ON d.SaleTime >= StartTime AND d.SaleTime < EndTime
) innerQuery
PIVOT (SUM(Amount) FOR Employee IN (' + @Headers + ')
) AS PivotTable
'
--PRINT @SQL -- Uncomment to see the query which will be run
EXECUTE(@SQL)
答案 1 :(得分:1)
您可以使用递归CTE动态创建时间段,然后将其加入您的数据。有一种方法可以避免使用CASE
,您可以使用PIVOT
命令,但我认为这更简单:
WITH CTE_TimeSlots AS
(
SELECT CAST('8:00' AS TIME) AS StartTime, CAST('8:30' AS TIME) AS EndTime
UNION ALL
SELECT DATEADD(MI, 30, StartTime), DATEADD(MI, 30, EndTime)
FROM CTE_TimeSlots
WHERE StartTime <= '12:00'
)
SELECT CONVERT(NVARCHAR(10),StartTime) + ' - ' + CONVERT(NVARCHAR(10),EndTime) AS [Time]
, CASE WHEN Employee = 'A' THEN Amount END AS A
, CASE WHEN Employee = 'B' THEN Amount END AS B
, CASE WHEN Employee = 'C' THEN Amount END AS C
FROM CTE_TimeSlots t
LEFT JOIN #test d ON d.SaleTime >= StartTime AND d.SaleTime < EndTime
<强> SQLFiddle DEMO 强>