【问题标题】:Writing single SQL query satisfying two cases编写满足两种情况的单个 SQL 查询
【发布时间】:2021-06-28 21:49:57
【问题描述】:

收到 cmets 后,我将用所需数据重新表述这个问题。

参考:SQL query to exclude some records from the output

Vertica 解析函数:https://www.vertica.com/blog/analytic-queries-vertica/

表 1:

create table etl_group_membership
(
    group_item_id int not null,
    member_item_id int not null
);

INSERT INTO etl_group_membership (group_item_id, member_item_id) VALUES (335640, 117722);
INSERT INTO etl_group_membership (group_item_id, member_item_id) VALUES (335640, 104151);
INSERT INTO etl_group_membership (group_item_id, member_item_id) VALUES (335640, 5316);

INSERT INTO etl_group_membership (group_item_id, member_item_id) VALUES (335641, 117723);
INSERT INTO etl_group_membership (group_item_id, member_item_id) VALUES (335641, 104152);
INSERT INTO etl_group_membership (group_item_id, member_item_id) VALUES (335641, 5317);

INSERT INTO etl_group_membership (group_item_id, member_item_id) VALUES (335642, 117724);
INSERT INTO etl_group_membership (group_item_id, member_item_id) VALUES (335642, 104153);
INSERT INTO etl_group_membership (group_item_id, member_item_id) VALUES (335642, 5318);

表 2:

create table v_poll_item
(
    device_item_id int not null,
    item_id int not null
    
);

INSERT INTO v_poll_item (device_item_id, item_id) VALUES (117722, 273215);
INSERT INTO v_poll_item (device_item_id, item_id) VALUES (117722, 117936);
INSERT INTO v_poll_item (device_item_id, item_id) VALUES (117722, 117873);
INSERT INTO v_poll_item (device_item_id, item_id) VALUES (117722, 123305);

INSERT INTO v_poll_item (device_item_id, item_id) VALUES (104151, 240006);
INSERT INTO v_poll_item (device_item_id, item_id) VALUES (104151, 240005);
INSERT INTO v_poll_item (device_item_id, item_id) VALUES (104151, 239415);
INSERT INTO v_poll_item (device_item_id, item_id) VALUES (104151, 239414);

INSERT INTO v_poll_item (device_item_id, item_id) VALUES (5316, 118310);
INSERT INTO v_poll_item (device_item_id, item_id) VALUES (5316, 130627);
INSERT INTO v_poll_item (device_item_id, item_id) VALUES (5316, 298564);
INSERT INTO v_poll_item (device_item_id, item_id) VALUES (5316, 118311);

表 3:注意 im_utilization 也可以为 NULL

create table nrm_cpustats_rate
(
    item_id int not null,
    tstamp datetime not null,
    im_utilization float,
);

INSERT INTO nrm_cpustats_rate (item_id, tstamp, im_utilization) VALUES (273215, '2021-06-28 19:55:00.000000', 2);
INSERT INTO nrm_cpustats_rate (item_id, tstamp, im_utilization) VALUES (273215, '2021-06-27 23:35:00.000000', 24);
INSERT INTO nrm_cpustats_rate (item_id, tstamp, im_utilization) VALUES (273215, '2021-06-26 14:05:00.000000', 27);
INSERT INTO nrm_cpustats_rate (item_id, tstamp, im_utilization) VALUES (273215, '2021-06-25 09:05:00.000000', 29);

INSERT INTO nrm_cpustats_rate (item_id, tstamp, im_utilization) VALUES (117936, '2021-06-28 19:30:00.000000', 17);
INSERT INTO nrm_cpustats_rate (item_id, tstamp, im_utilization) VALUES (117936, '2021-06-28 19:15:00.000000', 35);
INSERT INTO nrm_cpustats_rate (item_id, tstamp, im_utilization) VALUES (117936, '2021-06-28 19:05:00.000000', 50);
INSERT INTO nrm_cpustats_rate (item_id, tstamp, im_utilization) VALUES (117936, '2021-06-27 05:45:00.000000', 89);
INSERT INTO nrm_cpustats_rate (item_id, tstamp, im_utilization) VALUES (117936, '2021-06-25 09:20:00.000000', 37);
INSERT INTO nrm_cpustats_rate (item_id, tstamp, im_utilization) VALUES (117936, '2021-06-25 09:10:00.000000', 51);
INSERT INTO nrm_cpustats_rate (item_id, tstamp, im_utilization) VALUES (117936, '2021-06-25 08:50:00.000000', 90);

INSERT INTO nrm_cpustats_rate (item_id, tstamp, im_utilization) VALUES (118310, '2021-06-23 04:10:00.000000', 51);
INSERT INTO nrm_cpustats_rate (item_id, tstamp, im_utilization) VALUES (118310, '2021-06-23 03:15:00.000000', 48);
INSERT INTO nrm_cpustats_rate (item_id, tstamp, im_utilization) VALUES (118310, '2021-06-22 22:20:00.000000', 19);

INSERT INTO nrm_cpustats_rate (item_id, tstamp, im_utilization) VALUES (239414, '2021-06-22 17:10:00.000000', 11);
INSERT INTO nrm_cpustats_rate (item_id, tstamp, im_utilization) VALUES (239414, '2021-06-22 16:30:00.000000', 37);
INSERT INTO nrm_cpustats_rate (item_id, tstamp, im_utilization) VALUES (239414, '2021-06-22 16:35:00.000000', 38);
INSERT INTO nrm_cpustats_rate (item_id, tstamp, im_utilization) VALUES (239414, '2021-06-28 18:45:00.000000', 74);
INSERT INTO nrm_cpustats_rate (item_id, tstamp, im_utilization) VALUES (239414, '2021-06-28 18:48:00.000000', 76);
INSERT INTO nrm_cpustats_rate (item_id, tstamp, im_utilization) VALUES (239414, '2021-06-28 18:50:00.000000', 77);

INSERT INTO nrm_cpustats_rate (item_id, tstamp, im_utilization) VALUES (118311, '2021-06-28 00:40:00.000000', 29);
INSERT INTO nrm_cpustats_rate (item_id, tstamp, im_utilization) VALUES (118311, '2021-06-23 22:30:00.000000', 37);
INSERT INTO nrm_cpustats_rate (item_id, tstamp, im_utilization) VALUES (118311, '2021-06-23 22:25:00.000000', 92);

要获取组中的设备项 ID:

SELECT member_item_id FROM etl_group_membership WHERE group_item_id = 335640;

从检索到的设备item id列表中,获取item_ids列表:

SELECT item_id FROM v_poll_item WHERE device_item_id IN (<devices retrieved from previous query>);

输入:

  1. 两个时间范围:昨天到 7 天前 (AND tstamp > '2021-06-22 00:00:00.000000'AND tstamp <= '2021-06-28 23:59:59.000000')
  2. 组号:335640
  3. breach_threshold:25
  4. 每天最少违规次数:2

预期输出:

违反意味着 im_utilization >= 25

仅选择在给定日期内 count_of_breached >= 2 的记录 也就是说,item_id 为 273215 的记录被排除在外,因为即使违规次数 (>= 25) 为 2,但每天只有一个

device_item_id  |  item_id  |  count_of_breach  |  date_when_breached  |  max_utilization  |  max_utilization_tstamp
=====================================================================================================================
117722          |  117936   |  2                |  2021-06-28          | 90                |  2021-06-25 08:50:00.000000
117722          |  117936   |  3                |  2021-06-25          | 90                |  2021-06-25 08:50:00.000000
5316            |  118310   |  2                |  2021-06-23          | 51                |  2021-06-23 04:10:00.000000
5316            |  118311   |  2                |  2021-06-23          | 92                |  2021-06-23 22:25:00.000000
104151          |  239414   |  2                |  2021-06-22          | 77                |  2021-06-28 18:50:00.000000
104151          |  239414   |  3                |  2021-06-28          | 77                |  2021-06-28 18:50:00.000000

即使无法编写单个 SQL 查询来产生此输出,也可以建议两个优化查询。 @marcothesane 指出,即使是 query 也可以用更好的方式编写每日违规行为。

更新:

这个查询对我有用,可以在最大 im_utlization 和 tstamp 为最大值时找到它。我不确定为什么我必须在两个地方使用时间戳范围!

SELECT t.item_id, t.tstamp, t.im_utilization
FROM (
    SELECT item_id, MAX(im_utilization) AS max_cpu
    FROM nrm_cpustats_rate
    WHERE item_id IN (SELECT item_id from v_poll_item WHERE device_item_id IN (SELECT item_id FROM v_poll_item WHERE device_item_id IN (117722, 104151, 5316)))
    AND tstamp > '2021-06-22 00:00:00.000000
    AND tstamp <= '2021-06-28 23:59:59.000000'
    GROUP BY item_id
) AS m
INNER JOIN nrm_cpustats_rate AS t
ON t.item_id = m.item_id
AND t.im_utilization = m.max_cpu
AND tstamp > '2021-06-22 00:00:00.000000
AND tstamp <= '2021-06-28 23:59:59.000000'
ORDER BY 1, 2 DESC, 3 DESC

【问题讨论】:

  • 请用适当的 DBMS 标记您的问题。我看到很多 SQL Server 的无效语法。
  • 我标记了它。但是我认为如果我们去掉日期/时间相关的函数,SQL Server 的语法应该也是一样的。
  • 我几乎可以肯定你可以比你想出的更简化你的初始查询。但我不能尝试,因为您不提供输入数据。您如何分别获得 20,17 和 19 的 max?您可以从中间结果中粘贴一堆行吗?我认为 OLAP 函数的一些巧妙用法会有所帮助。但我不能这样尝试......
  • 我编辑了问题以使问题陈述更加详细。请看看现在这是否有意义。
  • 请提供表的创建语句,插入语句以获得足够的数据来说明需求和期望的输出。

标签: sql vertica


【解决方案1】:

首先,您的所有前期工作都特别依赖于 cpustats 速率表。但正如您所提到的,违规考虑基于同一天超过 1 个(因此为 2 个或更多)。因此,对于每个项目,每天(因此 date( ncr.tstamp ) 字段仅获取 DATE 部分而不管时间,我们可以应用 HAVING 子句,以便只有按项目和日期分组的那些有多个违规行为。

然后,我们可以加入特定设备 ID 的轮询项目表。以下内容应该适合您。

select 
        vpi.device_item_id,
        ncr.item_id,
        count(*) count_of_breach,
        date( ncr.tstamp ) date_when_breached,
        max( ncr.im_utilization ) max_utilization
    from
        nrm_cpustats_rate ncr
            join v_poll_item vpi
                on ncr.item_id = vpi.item_id
    where
        ncr.im_utilization >= 25
    group by
        vpi.device_item_id,
        ncr.item_id,
        date( ncr.tstamp )
    having
        count(*) > 1

反馈 - 修改

从查询中删除第二个表并尝试在查询中使用 TRUNC() 函数而不是 date()。还添加在您的日期过滤器限制中。

select 
        ncr.item_id,
        count(*) count_of_breach,
        trunc( ncr.tstamp, 'DD' ) date_when_breached,
        max( ncr.im_utilization ) max_utilization
    from
        nrm_cpustats_rate ncr
    where
            ncr.tstamp > '2021-06-22 00:00:00.000000'
        AND ncr.tstamp <= '2021-06-28 23:59:59.000000'
        AND ncr.im_utilization >= 25
    group by
        ncr.item_id,
        trunc( ncr.tstamp, 'DD' )
    having
        count(*) > 1

【讨论】:

  • 不幸的是,这不起作用!它首先给出vpi.device_item_id 应该是GROUP BY 的一部分的错误,然后在提供两个输入日期时不产生任何记录AND TO_TIMESTAMPTZ(tstamp) AT TIME ZONE 'UTC' &gt; TRUNC(sysdate - 7) AND TO_TIMESTAMPTZ(tstamp) AT TIME ZONE 'UTC' &lt;= TRUNC(sysdate - 1) + 1 - INTERVAL '1 SECOND' 输入日期是从7 天前到昨天。
  • @RajnishJain,抱歉,在拉取 device_item_id 时忘记添加新列。应该不错。由于我没有/使用 vertica-SQL,因此不能肯定 date() 函数是否正常工作。我认为它应该,如果不是,它可能需要更改为您的 TRUNC() 函数来截断日期/时间戳字段的时间部分。
  • 即使不使用任何时间戳范围,您建议的查询也不会产生任何结果。我编辑了问题以删除任何 Vertica 特定的日期/时间函数,并提供了两个输入时间戳作为日期时间。
  • @RajnishJain,修改答案并更改为 trunc(),先尝试第二个查询,然后将带有 JOIN 第二个表的部分拉回其中。
【解决方案2】:

您可以使用 ::DATE 操作将 TIMESTAMP 硬转换为 DATE。

和其他人一样:

您可以获取分组查询中的所有前五列,按 device_item_id,v_poll_item.item_id,tstamp::DATE 分组。我将它作为通用表表达式 (CTE) 放入 WITH 子句中。然后,我用nrm_cpustats_rateitem_id 和先前获得的max_utilizationim_utilization 的等式谓词加入了该CTE;最后我过滤掉了少于 2 个违规计数的行,并按时间戳范围再次过滤。

WITH
grp AS (
  SELECT
    device_item_id
  , v_poll_item.item_id
  , SUM(
      CASE 
        WHEN im_utilization >= 25 THEN 1
        ELSE 0
      END 
    ) AS count_of_breach
  , tstamp::DATE as date_when_breached
  , MAX(im_utilization) AS max_utilization
  FROM nrm_cpustats_rate
  JOIN v_poll_item USING(item_id)
  JOIN etl_group_membership ON member_item_id=device_item_id
  WHERE  tstamp > '2021-06-22 00:00:00.000000'AND tstamp <= '2021-06-28 23:59:59.000000'
  GROUP BY
    device_item_id
  , v_poll_item.item_id
  , tstamp::DATE
)
SELECT
  grp.*
, o.tstamp AS max_utilization_tstamp
FROM grp 
JOIN nrm_cpustats_rate o 
  ON o.item_id=grp.item_id 
 AND max_utilization=im_utilization
WHERE  tstamp > '2021-06-22 00:00:00.000000'AND tstamp <= '2021-06-28 23:59:59.000000'
  AND count_of_breach >= 2
;
-- out  device_item_id | item_id | count_of_breach | date_when_breached | max_utilization | max_utilization_tstamp 
-- out ----------------+---------+-----------------+--------------------+-----------------+------------------------
-- out            5316 |  118310 |               2 | 2021-06-23         |              51 | 2021-06-23 04:10:00
-- out          104151 |  239414 |               3 | 2021-06-28         |              77 | 2021-06-28 18:50:00
-- out          104151 |  239414 |               2 | 2021-06-22         |              38 | 2021-06-22 16:35:00
-- out          117722 |  117936 |               3 | 2021-06-25         |              90 | 2021-06-25 08:50:00
-- out            5316 |  118311 |               2 | 2021-06-23         |              92 | 2021-06-23 22:25:00
-- out          117722 |  117936 |               2 | 2021-06-28         |              50 | 2021-06-28 19:05:00

【讨论】:

  • 您创建的数据集是否与我在 INSERT 语句中提供的数据集不同?如果不是,则输出不匹配。我正在我的开发数据库中尝试这个,其中记录的数量要高得多。如果这有效,我会在这里更新。但是,非常感谢您抽出时间来研究这个问题。我真的很喜欢你计算“count_of_breach”的方式!!!
  • 不 - 我完全使用了您提供的数据 - 只是针对我的 Vertica 数据库运行了 CREATE TABLER 和 INSERT 语句...
  • 如果你没有改变数据集,那么输出是不是出乎意料?另外,这怎么可能不检查etc_group_membership 表中的每个member_item_id?也就是说,正如上面问题中提到的,我们将获得一个group_item_id 作为输入,然后必须从那里检索device_item_id 的列表,然后从那里检索item_id 的列表。请参阅上面输入部分上方的行。很抱歉,我不擅长 SQL,因此寻求帮助。
  • 我发现的唯一区别是,对于 device_item_id 117722,您有两次 max_utilization 90,而我有 90 和 50,对于 device_item_id 104151,您有两次 77,而我有 77 和 38。我认为我的结果更接近输入数据....
  • 谢谢。我有多个相同的最大值的原因是我重复了找到的最大值/时间戳。我觉得如果我们必须在同一个查询中获取所有内容,那么对于同一个项目 ID,最大值将重复,因为只能有一个最大值。是否也可以使用我在之前评论中提到的内容来调整查询?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-10
  • 2014-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-26
相关资源
最近更新 更多