【问题标题】:Count devices per day in a given date range在给定日期范围内每天计算设备数量
【发布时间】:2021-05-01 21:13:44
【问题描述】:

我有一个表,其中包含 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

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

示例: 我想生成一个堆叠条形图,它将显示通过、失败或警告的设备数量。需要获取一个查询,我可以使用它来获取回复,包括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

有没有更好的解决方案?

【问题讨论】:

  • 设备数是指每个设备的行数,对吧?请始终声明您的 Postgres 版本。 每日状态您的意思是.. 表中的每个设备每天一行?或者究竟是什么?

标签: sql postgresql aggregate aggregate-filter


【解决方案1】:

您可以使用聚合 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 表(您可能应该拥有),因此请即时生成完整列表。上面带有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 仅聚合给定日期范围内的行。它是严格可选的。您也可以直接左连接到基础表,然后聚合所有。但这种方法通常(很多)更快。

您可以使用 COALESCE / NULLIF 将 NULL 转换为零,反之亦然。

相关:

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

关于生成日期范围:

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

【讨论】:

  • 这看起来很棒。我怎样才能得到日期。基本上,我想获取开始日期和结束日期之间每天(日期)的失败、通过和警告的设备计数。
  • @Ash:每一天,好的。每天到底用什么设备?我更新以提供一种可能的解释。
  • 这是问题陈述 - 绘制堆积条形图,Y 轴将有设备计数,X 轴将有日期。用户可以选择一个日期范围,Y 轴将在该范围内的每个日期以堆叠条的形式显示设备、失败、通过和警告。
  • @Ash:好的,每天一个数据点。但是对于哪些设备?表中的所有设备/每天?还是时间范围内的所有设备?还是只针对那些碰巧有当天数据的设备?或者“设备数量”应该是指每天(不同?)设备的数量? (我在问题下的评论。)
  • 是的。表中的所有设备/每天 - 如果没有数据,则应显示空栏。但应在 x 轴上显示当天没有数据的日期。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-05
  • 1970-01-01
  • 2019-04-12
  • 1970-01-01
  • 2013-01-11
  • 1970-01-01
相关资源
最近更新 更多