如何计算每半小时?

时间:2016-08-24 00:10:19

标签: sql sql-server-2012

我有一个查询,它使用数据透视表每小时计数一次。 怎么可能每30分钟计算一次?

例如8:00-8:29,8:30-8:59,9:00-9:29等等,直到5:00

 SELECT CONVERT(varchar(8),start_date,1) AS 'Day',

       SUM(CASE WHEN DATEPART(hour,start_date) = 8 THEN 1 ELSE 0 END) as eight ,

       SUM(CASE WHEN DATEPART(hour,start_date) = 9 THEN 1 ELSE 0 END) AS nine,
       SUM(CASE WHEN DATEPART(hour,start_date) = 10 THEN 1 ELSE 0 END) AS ten,
       SUM(CASE WHEN DATEPART(hour,start_date) = 11 THEN 1 ELSE 0 END) AS eleven,
       SUM(CASE WHEN DATEPART(hour,start_date) = 12 THEN 1 ELSE 0 END) AS twelve,
       SUM(CASE WHEN DATEPART(hour,start_date) = 13 THEN 1 ELSE 0 END) AS one_clock,
       SUM(CASE WHEN DATEPART(hour,start_date) = 14 THEN 1 ELSE 0 END) AS two_clock,
       SUM(CASE WHEN DATEPART(hour,start_date) = 15 THEN 1 ELSE 0 END) AS three_clock,
       SUM(CASE WHEN DATEPART(hour,start_date) = 16 THEN 1 ELSE 0 END) AS four_clock

FROM  test
where user_id is not null 
GROUP BY CONVERT(varchar(8),start_date,1)
ORDER BY CONVERT(varchar(8),start_date,1)

我使用sql server 2012(版本Microsoft SQL Server Management Studio 11.0.3128.0)

3 个答案:

答案 0 :(得分:1)

尝试使用iif,如下所示:

SELECT CONVERT(varchar(8),start_date,1) AS 'Day', SUM(iif(DATEPART(hour,start_date) = 8 and
 DATEPART(minute,start_date) >= 0 and
 DATEPART(minute,start_date) =< 29,1,0)) as eight_tirty 
    FROM test where user_id is not null GROUP BY
 CONVERT(varchar(8),start_date,1) ORDER BY
 CONVERT(varchar(8),start_date,1)

答案 1 :(得分:1)

要按天和半小时计算,这样的事情应该有用。

SELECT day, half_hour, count(1) AS half_hour_count
FROM (
    SELECT 
        CAST(start_date AS date) AS day, 
        DATEPART(hh, start_date) 
            + 0.5*(DATEPART(n,start_date)/30) AS half_hour
    FROM test
    WHERE user_id IS NOT NULL
) qry
GROUP BY day, half_hour
ORDER BY day, half_hour;

格式化结果可以在以后完成。

答案 2 :(得分:1)

你需要一些东西,然后这个查询就会崩溃。

首先,假设您需要多个日期,您将需要所谓的Calendar Table(向下移动,可能是最有用的分析表)。

接下来,如果您有一个现有的Numbers表,或者只是动态生成第一个表,那么您将需要它:

WITH Halfs AS (SELECT CAST(0 AS INT) m
               UNION ALL
               SELECT m + 1
               FROM Halfs
               WHERE m < 24 * 2)
SELECT m
FROM Halfs

(递归CTE - 生成一个包含从0开始的数字列表的表)。

这两个表将根据主表中的时间戳为范围查询提供基础。这将使优化器很容易为您正在进行的任何聚合打包行。这是通过CROSS JOIN在子查询中将两个表放在一起完成的,并添加了几个其他派生列:

WITH Halfs AS (SELECT CAST(0 AS INT) m
               UNION ALL
               SELECT m + 1
               FROM Halfs
               WHERE m < 24 * 2)

SELECT calendarDate, m, rangeStart, rangeEnd
FROM (SELECT Calendar.calendarDate, Halfs.m rangeGroup,
             DATEADD(minutes, m * 30, CAST(Calendar.calendarDate AS DATETIME2) rangeStart,
             DATEADD(minutes, (m + 1) * 30, CAST(Calendar.calendarDate AS DATETIME2) rangeEnd                 
      FROM Calendar
      CROSS JOIN Halfs
      WHERE Calendar.calendarDate >= CAST('20160823' AS DATE)
            AND Calendar.calendarDate < CAST('20160830' AS DATE)
            -- OR whatever your date range actually is.
     ) Range
ORDER BY rangeStart

(请注意,如果日期范围足够大,将其保存为带有标记的临时表可能是有益的。对于小表和数据集,性能增益可能不太明显)

既然我们已经拥有了我们的范围,那么获得我们的团队并转动表格是微不足道的 哦,SQL Server有PIVOTing的特定运算符。

WITH Halfs AS (SELECT CAST(0 AS INT) m
               UNION ALL
               SELECT m + 1
               FROM Halfs
               WHERE m < 3 * 2)
               -- Intentionally limiting range for example only

SELECT calendarDate AS day, [0], [1], [2], [3], [4], [5], [6]
                            -- If you're displaying "nice" names,
                            -- do it at this point, or in the reporting application
FROM (SELECT Range.calendarDate, Range.rangeGroup
      FROM (SELECT Calendar.calendarDate, Halfs.m rangeGroup,
                   DATEADD(minutes, m * 30, CAST(Calendar.calendarDate AS DATETIME2) rangeStart,
                   DATEADD(minutes, (m + 1) * 30, CAST(Calendar.calendarDate AS DATETIME2) rangeEnd                 
                   FROM Calendar
                   CROSS JOIN Halfs
                   WHERE Calendar.calendarDate >= CAST('20160823' AS DATE)
                         AND Calendar.calendarDate < CAST('20160830' AS DATE)
                         -- OR whatever your date range actually is.
                   ) Range
      LEFT JOIN Test
             ON Test.user_id IS NOT NULL
                AND Test.start_date >= Range.rangeStart
                AND Test.start_date < Range.rangeEnd
     ) AS DataTable
PIVOT (COUNT(*)
       FOR Range.rangeGroup IN ([0], [1], [2], [3], [4], [5], [6])) AS PT
                                -- Only covers the first 6 groups, 
                                -- or the first three hours.  
ORDER BY day

数据透视表应该负责获取各个列,COUNT将自动解析空行。应该是你所需要的一切。