如何在SQL中计算两个和的差

Posted

技术标签:

【中文标题】如何在SQL中计算两个和的差【英文标题】:How to calculate the difference of two sums in SQL 【发布时间】:2021-11-12 05:48:33 【问题描述】:

我正在使用 Grafana 显示来自 Clickhouse 的一些数据。数据来自包含itimecount 和其他一些列的表。

id  method   count        itime
1    aaa      12     2021-07-20 00:07:06
2    bbb      9      2021-07-20 00:07:06
3    ccc      7      2021-07-20 00:07:07
...

现在我可以执行以下 SQL 来获得两个 itimes 之间的 count 之和:

SELECT toUnixTimestamp(toStartOfMinute(itime)) * 1000 as t,
       method,
       sum(count) as c
FROM me.my_table
WHERE itime BETWEEN toDateTime(1631870605) AND toDateTime(1631874205)
      and method like 'a%'
GROUP BY method, t
HAVING c > 500
ORDER BY t

它按预期工作。

现在,我想根据sum(count) - sum(count)<--7-day-ago之间的差异选择sum(count)。像SELECT ... FROM ... WHERE ... HAVING c - c<--7-day-ago >= 100 这样的东西。但我不知道怎么做。

【问题讨论】:

您能否简化您的查询并删除您创建的函数(如果我们不需要它们)?另外,您能否给我们一些更多数据(相关数据)和该数据的预期结果(因为您说 7 天前......并且示例都在同一天......) @VBoka 好吧,事实上我正在与 Grafana 和 Clickhouse 合作......好吧,除了 itime 发生了变化之外,数据就是这样。我确信它存储了大量数据,包括 7 天前的数据。 @Akina 我是 DB 的新手。我以为他们都一样。事实上,我正在与 Grafana 和 Clickhouse 合作。 【参考方案1】:

我之前也遇到过类似的问题

请查看SQLfiddle 查看结果按下按钮:第一个 - build schema,第二个:run sql

命名

我假设您希望在同一时间段 A 中选择 7 天后的时间段 B 进行比较(您需要更具体,您真正在寻找什么)。

期间 A = 您选择的时间段(fromto 之间) 期间 B = 您选择的过去一周内的时间段

问题

如果我理解正确的话,这是一个非常微妙的问题。 您的示例在周期 A 内按分钟分组。这意味着,对于周期 B 中有数据的每一分钟,您确实需要周期 A 中的数据,否则您将忽略所选周期内的周期 B 数据。

正如您在 sqlfiddle 中看到的,我创建了两个查询字符串。第一个正在工作,但忽略了 B 数据。第二个进行右连接(遗憾的是 mysql 不支持完全外连接以在一个表中显示所有内容)并显示 2 个被忽略的条目。

这甚至使情况变得更糟,因为您也按方法分组。 (在这种情况下,您必须更改连接的最后一行并添加:)

as b on a.unix_itime = b.unix_itime and a.method = b.method

这意味着,您需要每个选定的方法和周期的分钟数据。

如果你只按方法而不是时间分组会更好,因为你已经使用时间条件(周期 A)来保持它的小。 或按小时或按天加大步数..

此代码应该适合您的环境(mysql 不支持 toUnixTimestamp、toStartOfMinute、toDateTime):

SELECT 
    a.unix_itime * 1000 as t,
    a.method,
    a.sum AS c,
    b.sum AS c2,
    ifnull(a.sum,0) - ifnull(b.sum,0) as diff,
    
FROM (select method, sum(count) as sum, toUnixTimestamp(toStartOfMinute(itime)) as unix_itime
      from my_table
      WHERE method like 'a%' and
          itime BETWEEN toDateTime(1631870605) 
                    AND toDateTime(1631874205)
      GROUP BY method, unix_itime) 
          as a

  LEFT JOIN (select method, sum(count) as sum, toUnixTimestamp(toStartOfMinute(itime + INTERVAL 7 DAY)) as unix_itime
            from my_table
            WHERE method like 'a%' and
                itime BETWEEN toDateTime(1631870605)- INTERVAL 7 DAY 
                          AND toDateTime(1631874205)- INTERVAL 7 DAY
            GROUP BY method, unix_itime) 
                as b on a.unix_itime = b.unix_itime and a.method = b.method

ORDER BY a.unix_itime;

【讨论】:

【参考方案2】:

逻辑有点模棱两可,但这可能会产生上述一种可能的含义。如果您仍想返回整体 SUM(count),只需将其添加到选择列表中即可。

SELECT toUnixTimestamp(toStartOfMinute(itime)) * 1000 AS t
     , method
     , SUM(count) AS c
     , SUM(count) - SUM(CASE WHEN itime < current_date - INTERVAL 7 DAY THEN count END) AS c2
  FROM me.my_table
 WHERE method like 'a%'
 GROUP BY method, t
HAVING c2 >= 100
 ORDER BY t
;

根据需要进行调整。

也许您不想返回差异,只是过滤返回的组。如果是这样,试试这个:

SELECT toUnixTimestamp(toStartOfMinute(itime)) * 1000 AS t
     , method
     , SUM(count) AS c
  FROM me.my_table
 WHERE method like 'a%'
 GROUP BY method, t
HAVING SUM(count) - SUM(CASE WHEN itime < current_date - INTERVAL 7 DAY THEN count END) >= 100
 ORDER BY t
;

【讨论】:

【参考方案3】:
create table test(D Date, Key Int64, Val Int64) Engine=Memory;

insert into test select today(), number, 100 from numbers(5);

insert into test select today()-7, number, 110 from numbers(5);

select sx.2 d1, Key, sumIf(sx.1, D=sx.2) s, sumIf(sx.1, D!=sx.2) s1 from (
select D, Key, arrayJoin([(s, D), (s, D + interval 7 day)]) sx
from (select D, Key, sum(Val) s from test group by D, Key)
)group by d1, Key
order by d1, Key;

┌─────────d1─┬─Key─┬───s─┬──s1─┐
│ 2021-09-10 │   0 │ 110 │   0 │
│ 2021-09-10 │   1 │ 110 │   0 │
│ 2021-09-10 │   2 │ 110 │   0 │
│ 2021-09-10 │   3 │ 110 │   0 │
│ 2021-09-10 │   4 │ 110 │   0 │
│ 2021-09-17 │   0 │ 100 │ 110 │
│ 2021-09-17 │   1 │ 100 │ 110 │
│ 2021-09-17 │   2 │ 100 │ 110 │
│ 2021-09-17 │   3 │ 100 │ 110 │
│ 2021-09-17 │   4 │ 100 │ 110 │
│ 2021-09-24 │   0 │   0 │ 100 │
│ 2021-09-24 │   1 │   0 │ 100 │
│ 2021-09-24 │   2 │   0 │ 100 │
│ 2021-09-24 │   3 │   0 │ 100 │
│ 2021-09-24 │   4 │   0 │ 100 │
└────────────┴─────┴─────┴─────┘
SELECT
    D,
    Key,
    Val,
    any(Val) OVER (PARTITION BY Key ORDER BY D ASC RANGE BETWEEN 7 PRECEDING AND 7 PRECEDING) Val1
FROM test

┌──────────D─┬─Key─┬─Val─┬─Val1─┐
│ 2021-09-10 │   0 │ 110 │    0 │
│ 2021-09-17 │   0 │ 100 │  110 │
│ 2021-09-10 │   1 │ 110 │    0 │
│ 2021-09-17 │   1 │ 100 │  110 │
│ 2021-09-10 │   2 │ 110 │    0 │
│ 2021-09-17 │   2 │ 100 │  110 │
│ 2021-09-10 │   3 │ 110 │    0 │
│ 2021-09-17 │   3 │ 100 │  110 │
│ 2021-09-10 │   4 │ 110 │    0 │
│ 2021-09-17 │   4 │ 100 │  110 │
└────────────┴─────┴─────┴──────┘

【讨论】:

以上是关于如何在SQL中计算两个和的差的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode 2035. 将数组分成两个数组并最小化数组和的差

将数组分成两个数组并最小化数组和的差(折半搜索+二分)

leetcode 2035. 将数组分成两个数组并最小化数组和的差

如何通过Period类计算两个日期的差?

excel 日期格式为 "yyyymmdd hhmm" 如何计算两个时间的差?

sql计算两个时间之间的差,并用时分秒表示