按小时分组行的更有效方法(使用时间戳)

时间:2013-03-01 21:54:24

标签: sql-server-2012

我正在尝试显示发生的每日交易记录。我目前的方法效率低得令人尴尬,我确信有更好的解决方案。这是我目前的查询:

select ReaderMACAddress,
count(typeid) as 'Total Transactions',
SUM(CASE WHEN CAST("Timestamp" as TIME) between '05:00:00' and '11:59:59' THEN 1 ELSE 0 END) as 'Morning(5am-12pm)',
SUM(CASE WHEN CAST("Timestamp" as TIME) between '12:00:00' and '17:59:59' THEN 1 ELSE 0 END) as 'AfternoonActivity(12pm-6pm)',
SUM(CASE WHEN CAST("Timestamp" as TIME) between '18:00:00' and '23:59:59' THEN 1 ELSE 0 END) as 'EveningActivity(6pm-12am)',
SUM(CASE WHEN CAST("Timestamp" as TIME) between '00:00:00' and '04:59:59' THEN 1 ELSE 0 END) as 'OtherActivity(12am-5am)'
from Transactions
where ReaderMACAddress = '0014f54033f5'
Group by ReaderMACAddress;

返回结果:

ReaderMACAddress    Total Transactions  Morning(5am-12pm)   AfternoonActivity(12pm-6pm) EveningActivity(6pm-12am)   OtherActivity(12am-5am)
0014f54033f5               932                269                    431                          232                         0

(抱歉这里有任何对齐问题)

目前我只想查看我指定的单个Reader(通过where子句)。理想情况下,如果时间段位于单个列中并且结果(即计数函数位于第二列中)会产生如下结果,则会更容易阅读:

Total Transactions          932
Morning(5am-12pm)           269
AfternoonActivity(12pm-6pm) 431
EveningActivity(6pm-12am)   232
OtherActivity(12am-5am)     0

感谢您的帮助:)

1 个答案:

答案 0 :(得分:6)

我首先会考虑一个计算列,但我相信从之前的帖子中你无法更改架构。那么一个观点呢?

CREATE VIEW dbo.GroupedReaderView
AS
  SELECT ReaderMACAddress,
    Slot = CASE WHEN t >= '05:00' AND t < '12:00' THEN 1
                WHEN t >= '12:00' AND t < '18:00' THEN 2
                WHEN t >= '18:00' THEN 3 ELSE 4 END
  FROM 
  (
    SELECT ReaderMACAddress, t = CONVERT(TIME, [Timestamp])
     FROM dbo.Transactions
  ) AS x;

现在,您的每MAC地址查询要简单得多:

SELECT Slot, COUNT(*)
  FROM dbo.GroupedReaderView
  WHERE ReaderMACAddress = '00...'
  GROUP BY Slot;

这将提供如下结果:

1    269
2    431
3    232
4      0

您还可以添加WITH ROLLUP,其中Slot列的NULL列为SELECT Slot, COUNT(*) FROM dbo.GroupedReaderView WHERE ReaderMACAddress = '00...' GROUP BY Slot WITH ROLLUP;

1     269
2     431
3     232
4       0
NULL  932

应该屈服:

CREATE VIEW dbo.GroupedReaderView
AS
  SELECT ReaderMACAddress,
    Slot = CASE WHEN t >= '05:00' AND t < '12:00' THEN 
                   'Morning(5am-12pm)'
                WHEN t >= '12:00' AND t < '18:00' THEN 
                   'Afternoon(12pm-6pm)'
                WHEN t >= '18:00' THEN 
                   'Evening(6pm-12am)'
                ELSE 
                   'Other(12am-5am)' 
                END
  FROM 
  (
    SELECT ReaderMACAddress, t = CONVERT(TIME, [Timestamp])
     FROM dbo.Transactions
  ) AS x;

如果需要,您可以在您的演示文稿层中为每个插槽添加标签等。

你也可以这样做,它只是使视图更加冗长,并在你直接查询时提取大量额外数据;按字符串分组效率也稍差。

SELECT Slot, COUNT(*)
  FROM 
  (
    SELECT ReaderMACAddress,
      Slot = CASE WHEN t >= '05:00' AND t < '12:00' THEN 
                   'Morning(5am-12pm)'
                  WHEN t >= '12:00' AND t < '18:00' THEN 
                   'Afternoon(12pm-6pm)'
                  WHEN t >= '18:00' THEN 
                   'Evening(6pm-12am)'
                  ELSE 
                   'Other(12am-5am)' 
                  END
    FROM 
    (
      SELECT ReaderMACAddress, t = CONVERT(TIME, [Timestamp])
       FROM dbo.Transactions
    ) AS x
  ) AS y
  WHERE ReaderMACAddress = '00...'
  GROUP BY Slot
  WITH ROLLUP;

这些并不一定比你所获得的更有效,但它们的重复性更低,更容易看到。 : - )

此外,如果您不想(或不能)创建视图,您可以将其放入子查询中,例如:

SELECT Slot, COUNT(*)
  FROM 
  (
    SELECT ReaderMACAddress,
      Slot = CASE WHEN h BETWEEN 5  AND 11 THEN 'Morning(5am-12pm)'
                  WHEN h BETWEEN 12 AND 17 THEN 'Afternoon(12pm-6pm)'
                  WHEN h >= 18 THEN 'Evening(6pm-12am)'
                  ELSE 'Other(12am-5am)' 
                  END
    FROM 
    (
      SELECT ReaderMACAddress, h = DATEPART(HOUR, [Timestamp])
       FROM dbo.Transactions
    ) AS x
  ) AS y
  WHERE ReaderMACAddress = '00...'
  GROUP BY Slot
  WITH ROLLUP;

只是一个替代方案,仍然允许你使用BETWEEN,甚至可能更简洁:

;WITH slots(s, label, h1, h2) AS
(
  SELECT           1, 'Morning(5am-12pm)'   , 5,  11
  UNION ALL SELECT 2, 'Afternoon(12pm-6pm)' , 12, 17
  UNION ALL SELECT 3, 'Evening(6pm-12am)'   , 18, 23
  UNION ALL SELECT 4, 'Other(12am-5am)'     , 0,  4
)
SELECT s.label, c = COALESCE(COUNT(y.ReaderMACAddress), 0)
  FROM slots AS s
  LEFT OUTER JOIN
  (
     SELECT ReaderMACAddress, h = DATEPART(HOUR, [Timestamp])
       FROM dbo.Transactions
       WHERE ReaderMACAddress = '00...'
  ) AS y
 ON y.h BETWEEN s.h1 AND s.h2
 GROUP BY s.label
 WITH ROLLUP;

<强>更新

即使该插槽没有结果,也要始终包含每个插槽:

{{1}}

所有这些案例中的关键是简化而不是重复自己。即使SQL Server只执行一次,为什么转换为时间超过4次?