SQL按时间透视数据

时间:2013-07-24 11:01:24

标签: sql sql-server-2008 tsql

我正在尝试根据时间戳获取数据的数据透视。我想将它们分成半小时的“水桶”。例如,使用以下数据:

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?

谢谢!

2 个答案:

答案 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