计算给定日期范围内每天的设备数

Count devices per day in a given date range

我有一个 table 设备有 3 种状态,通过、失败和警告。

Device Status Date
Device1 Pass 12/1/2020
Device2 Fail 12/1/2020
Device3 Warning 12/1/2020
Device1 Fail 12/2/2020
Device2 Warning 12/2/2020
Device3 Pass 12/2/2020

我想根据每天的状态生成设备数量的趋势图。每天都在所有设备上进行计数。上面的 table 将在多个日期重复设备数据。

示例: 我想生成一个堆叠条形图,它将显示通过、失败或警告的设备数。需要获取一个查询,我可以使用它来获取响应 DateTime、失败设备的数量、通过的设备数量、在日期范围内发出警告的设备数量。

select * (select count(*) from status_table where overall_status = 'Fail' and startDate > "" and endDate < "") as failedCount,
(select count(*) from status_table  where overall_status = 'Warning' and startDate > "" and endDate < "") as WarningCount,
(select count(*) from status_table  where overall_status = 'Pass'  startDate > "" and endDate < "") as passCount from status_table

有更好的解决方案吗?

您可以使用聚合 FILTER 子句在单个查询中执行此操作。

这会在选定日期范围内的每一天为每个选定设备获取三个计数(失败、通过、警告)。没有任何出现的天数为 NULL。 0 如果设备出现,但不具有此状态:

SELECT date, device_name
     , fail_count, warning_count, pass_count
FROM  (SELECT DISTINCT device_name FROM status_table) d  -- all devices ①
CROSS  JOIN (
   SELECT generate_series(timestamp '2020-12-01'
                        , timestamp '2020-12-31'
                        , interval  '1 day')::date
   ) t(date)  -- all dates
LEFT   JOIN (
   SELECT date, device_name
        , count(*) FILTER (WHERE overall_status = 'Fail')    AS fail_count
        , count(*) FILTER (WHERE overall_status = 'Warning') AS warning_count
        , count(*) FILTER (WHERE overall_status = 'Pass')    AS pass_count 
   FROM   status_table
   WHERE  date >= '2020-12-01'  -- same date range as above
   AND    date <= '2020-12-31'
   GROUP  BY 1, 2
   ) s USING (date, device_name)
ORDER  BY 1, 2;

基本上,您 CROSS JOIN 所有设备到所有日期(笛卡尔积),附加数据可以在其中找到数据 LEFT JOIN

① 由于您似乎没有 device table(您可能应该有),请即时生成完整列表。上面带有 DISTINCT 的查询适用于每个设备的几行。否则,还有(很多)更快的技术,例如:

WITH RECURSIVE cte AS (
   (SELECT device_name FROM status_table ORDER BY 1 LIMIT 1)
   UNION ALL
   SELECT (SELECT device_name FROM status_table
           WHERE  device_name > t.device_name ORDER BY 1 LIMIT 1)
   FROM   cte
   WHERE  device_name IS NOT NULL
   )
SELECT * FROM cte
WHERE  device_name IS NOT NULL;

参见:

子查询 s 仅聚合给定日期范围内的行。它是严格可选的。也可以直接left-join到底层的table,然后聚合所有。但是这种方法通常(快得多)。

您可以使用 COALESCE / NULLIF.

将 NULL 转换为零,反之亦然

相关:

  • PostgreSQL: running count of rows for a query 'by minute'

  • Aggregate columns with additional (distinct) filters

对于更多标志,crosstab() 查询可能会更快。参见:

  • PostgreSQL Crosstab Query

关于生成日期范围:

  • Generating time series between two dates in PostgreSQL

请注意,如果您使用 timestamp with time zone 操作,日期将由您当前的时区设置定义。参见:

  • Ignoring time zones altogether in Rails and PostgreSQL