【问题标题】:SQL average time between first and second row in a set一组中第一行和第二行之间的 SQL 平均时间
【发布时间】:2020-01-15 21:47:55
【问题描述】:

哎哟!我听到很痛..以为我有几次但史诗般的失败:(

我有以下数据,数百万行,索引,MySQL 5.6。

在这张表中,有一组数据,而uuid基本上就是每组数据的唯一id。

我需要在每组中的每个第一行和第二行的数据中找到 AVERAGE。换句话说,自使用同一组的第一个插入和第二个插入创建该组以来经过了多长时间,然后是结果的平均值。

我可以得到平均值没问题,但我似乎无法找到一种方法来计算每组中第一行和第二行之间的时间差。

我什至不会让自己尴尬,并使用子查询和 LIMIT 错误地尝试粘贴我损坏的 SQL,我只想说,这个让我逃脱了。

任何帮助表示赞赏,我的啤酒:/

+------+-----------------------------------------+----------------------------+ | id | uuid | stamp | +------+-----------------------------------------+----------------------------+ | 707 | 60b5-d062-5829-c11d-5b71-5d85-075b-a3c5 | 2020-01-01 17:00:28.000000 | | 708 | 60b5-d062-5829-c11d-5b71-5d85-075b-a3c5 | 2020-01-01 17:01:30.000000 | | 709 | 0ccf-94e0-ce72-8092-1975-5bea-6131-c719 | 2020-01-02 14:11:48.000000 | | 710 | 59c8-60ee-d172-511a-a477-c637-6789-f14a | 2020-01-02 14:23:36.000000 | | 711 | b33b-7584-1fed-e138-28ba-c24a-9b46-88e7 | 2020-01-02 14:24:07.000000 | | 712 | eddc-b12a-5ef2-baea-cf53-7287-5805-d922 | 2020-01-02 14:24:26.000000 | | 713 | 257b-fc66-6d7a-ba21-727e-1da7-0ee1-714c | 2020-01-02 14:25:31.000000 | | 718 | c5d9-acba-9a12-aacb-cf45-c5a9-2b8d-314c | 2020-01-02 15:46:41.000000 | | 719 | 0ccf-94e0-ce72-8092-1975-5bea-6131-c719 | 2020-01-02 15:55:42.000000 | | 720 | c5d9-acba-9a12-aacb-cf45-c5a9-2b8d-314c | 2020-01-02 15:56:33.000000 | | 722 | c5d9-acba-9a12-aacb-cf45-c5a9-2b8d-314c | 2020-01-02 16:16:14.000000 | | 723 | c5d9-acba-9a12-aacb-cf45-c5a9-2b8d-314c | 2020-01-02 16:21:25.000000 | | 726 | 6610-a9df-358d-0065-beb8-cea1-82a6-3258 | 2020-01-02 17:16:33.000000 | | 727 | 6610-a9df-358d-0065-beb8-cea1-82a6-3258 | 2020-01-02 17:21:20.000000 | | 728 | 6610-a9df-358d-0065-beb8-cea1-82a6-3258 | 2020-01-02 17:45:07.000000 | | 729 | 6610-a9df-358d-0065-beb8-cea1-82a6-3258 | 2020-01-02 17:50:17.000000 | | 730 | 6610-a9df-358d-0065-beb8-cea1-82a6-3258 | 2020-01-02 18:14:02.000000 | | 731 | 6610-a9df-358d-0065-beb8-cea1-82a6-3258 | 2020-01-02 18:27:48.000000 | | 732 | 6610-a9df-358d-0065-beb8-cea1-82a6-3258 | 2020-01-02 18:28:57.000000 | | 733 | c193-a46f-1104-3ee3-7387-94a8-ef32-a85e | 2020-01-02 18:40:40.000000 | | 734 | c193-a46f-1104-3ee3-7387-94a8-ef32-a85e | 2020-01-02 18:40:49.000000 |

【问题讨论】:

  • 预期输出是什么?只有一个值?
  • 最终,我在寻找集合中前两行之间当天的总平均时间......很容易将这些数据视为一个庞大的帮助台系统,基本上是平均初始响应时间那天,每张“票”都是 uuid。任何帮助表示赞赏:)
  • 通常最好在这里展示一些更友好的东西。 6610-a9df-358d-0065-beb8-cea1-82a6-3258 可能适合您的业务案例,但不是很友好。

标签: mysql sql mysql-5.6


【解决方案1】:

如果一个用户 id 只出现了两次,那么这是微不足道的。您有数百万行,所以让我们尽量避免排序并假设您有正确的索引。

这是获取最早的两行的一种方法:

select t.*
from t
where t.stamp <= (select t2.stamp
                  from t t2
                  where t2.uuid = t.uuid
                  order by t2.stamp asc
                  limit 1,1
                 );

非常重要:您希望在(uuid, stamp) 上建立索引,以获得任何性能希望。

然后,只是聚合:

select uuid, timestampdiff(second, min(stamp), max(stamp))
from (select t.*
      from t
      where t.stamp <= (select t2.stamp
                        from t t2
                        where t2.uuid = t.uuid
                        order by t2.stamp asc
                        limit 1,1
                       )
     ) t
group by uuid;

【讨论】:

    【解决方案2】:

    另一种方法是使用 LEFT JOIN 代替子查询。

    SELECT
          t.uuid
        , t.stamp AS t_stamp
        , t_next.stamp AS t_next_stamp
        , TIME_TO_SEC(TIMEDIFF(t_next.stamp, t.stamp)) AS diff
    FROM
        ttt AS t
        LEFT JOIN ttt AS t_prev ON (
                t_prev.uuid  = t.uuid
            AND t_prev.stamp < t.stamp
        )
        INNER JOIN ttt AS t_next ON (
                t_next.uuid  = t.uuid
            AND t_next.stamp > t.stamp
        )
        LEFT JOIN ttt AS t_before_next ON (
                t_before_next.uuid  = t.uuid
            AND t_before_next.stamp > t.stamp
            AND t_before_next.stamp < t_next.stamp 
        )
    WHERE
            t_prev.id IS NULL -- no t_prev so t is the first record
        AND t_before_next.id IS NULL -- no t_before_next so t_next is the second record
        -- filter data by your criteria, per day for example.
        -- you will need to "duplicate" filtering conditions for t_prev and t_next
    ORDER BY
        uuid
    

    =>

    uuid    t_stamp t_next_stamp    diff
    0ccf-94e0-ce72-8092-1975-5bea-6131-c719 2020-01-02 14:11:48 2020-01-02 15:55:42 6234
    60b5-d062-5829-c11d-5b71-5d85-075b-a3c5 2020-01-01 17:00:28 2020-01-01 17:01:30 62
    6610-a9df-358d-0065-beb8-cea1-82a6-3258 2020-01-02 17:16:33 2020-01-02 17:21:20 287
    c193-a46f-1104-3ee3-7387-94a8-ef32-a85e 2020-01-02 18:40:40 2020-01-02 18:40:49 9
    c5d9-acba-9a12-aacb-cf45-c5a9-2b8d-314c 2020-01-02 15:46:41 2020-01-02 15:56:33 592
    

    警告:

    上面的查询将丢失具有相同标记的记录。如果您需要它们,则必须更改连接条件:

    来自

    t_prev.stamp < t.stamp
    

    t_prev.stamp <= t.stamp AND t_prev.id < t.id
    

    然后就可以使用查询来获取AVG了:

    -- explain
    SELECT
        AVG(TIME_TO_SEC(TIMEDIFF(t_next.stamp, t.stamp))) AS avg_diff
    FROM
        ttt AS t
        LEFT JOIN ttt AS t_prev ON (
                t_prev.uuid  = t.uuid
            AND t_prev.stamp < t.stamp
        )
        INNER JOIN ttt AS t_next ON (
                t_next.uuid  = t.uuid
            AND t_next.stamp > t.stamp
        )
        LEFT JOIN ttt AS t_before_next ON (
                t_before_next.uuid  = t.uuid
            AND t_before_next.stamp > t.stamp
            AND t_before_next.stamp < t_next.stamp 
        )
    WHERE
            t_prev.id IS NULL
        AND t_before_next.id IS NULL
    

    => 1436.8000(用于您的数据集)

    用复合索引(uuid、stamp)解释:

    id  select_type table   partitions  type    possible_keys   key key_len ref rows    filtered    Extra
    1   SIMPLE  t   NULL    index   ix_uuid_stamp   ix_uuid_stamp   49  NULL    21  100.00  Using where; Using index
    1   SIMPLE  t_prev  NULL    ref ix_uuid_stamp   ix_uuid_stamp   43  test.t.uuid 2   10.00   Using where; Not exists; Using index
    1   SIMPLE  t_next  NULL    ref ix_uuid_stamp   ix_uuid_stamp   43  test.t.uuid 2   33.33   Using where; Using index
    1   SIMPLE  t_before_next   NULL    ref ix_uuid_stamp   ix_uuid_stamp   43  test.t.uuid 2   10.00   Using where; Not exists; Using index
    

    使用“ref”代替接受答案中的“依赖子查询”。 什么更好取决于您的数据。 如果过滤的数据集(当您按天过滤记录时)很小,“依赖子查询”会更快。在过滤过的大数据集上,我更喜欢使用“ref”。

    请随意测试这两种方式,让我们知道哪种方式在您的情况下更快。

    【讨论】:

      猜你喜欢
      • 2021-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-12
      • 2021-03-15
      • 2023-04-02
      相关资源
      最近更新 更多