【问题标题】:Query records from same table not present in last 30 days过去 30 天内不存在同一张表的查询记录
【发布时间】:2015-10-03 17:46:29
【问题描述】:

我有一个名为页面加载的表格,其中包含以下列

user_id
timestamp
country
city

该表有 3700 万条记录,两列都有索引

时间戳范围从 7/23 到 9/27

我需要做的是:对于 8/24 到 9/27 的日期范围,每天计算过去 30 天表中不存在的不同用户。

因此,对于 8 月 24 日存在的表中的用户,查找 7 月 23 日到 8 月 23 日之间表中不存在的不同用户的计数。 8/24 到 9/27 之间的每一天都需要这样做。

select
    count(distinct user_id)
from
    pageloads
where
    user_id not in (select user_id from pageloads where time between
'2015-07-24 00:00:00' and '2015-08-23 23:59:59')
and left(time,10) = '2015-08-24'

由于 not in 子句,这种方法非常慢,并且一次运行 1 天。

谁能帮帮我。

【问题讨论】:

    标签: mysql sql


    【解决方案1】:

    如果你有子查询,IN子句非常慢。最好使用临时表和连接,它会快得多。

    【讨论】:

      【解决方案2】:

      尝试使用

      AND NOT EXIST ( select 1 from pageloads p1 where  p1.time between
      '2015-07-24 00:00:00' and '2015-08-23 23:59:59' and p1.user_id = user_id)
      

      NOT EXIST 非常有效,因为它会在找到 1 条记录时停止搜索。如果没有记录,那么它也将立即被发现。 user_id 上的索引也可以使用。

      【讨论】:

        【解决方案3】:

        我进行了一些测试,并找到了最好的解决方案。我现在不知道您多久生成一次此报告,但如果您使用新的累积表,我会得到最好的结果。

        在此表中,您存储每天/用户的累计值,如果您想在第二天生成报告,只需使用一天的数据更新新表。

        新表格

        CREATE TABLE `pageload_cum` (
          `user_id` INT(11) NOT NULL DEFAULT '0',
          `time` DATE,
          `quantity` INT(11) DEFAULT NULL,
          PRIMARY KEY (`user_id`,`time`),
          KEY `time` (`time`,`user_id`)
        ) ENGINE=INNODB DEFAULT CHARSET=utf8;
        

        填表

        此查询将插入过去 60 天的所有数据,这些数据将用于从昨天开始的报告。如果你明天开始它只会产生 失踪的一天

        INSERT IGNORE INTO pageload_cum
        SELECT DISTINCT p.user_id ,
          DATE(p.`time`),
          SUM(1) AS quantity 
        FROM pageloads p
        WHERE
         DATE(p.`time`) NOT IN (
           SELECT DISTINCT p.time FROM pageload_cum p
         )
        AND p.`time` > SUBDATE(NOW(), INTERVAL 61 DAY)
        AND p.`time` < SUBDATE(NOW(), INTERVAL 1 DAY)
        GROUP BY DATE(p.`time`), p.user_id;
        

        现在是报告

        报告会生成一些仅用于了解结果的列。如果您不使用它们,您可以删除它们。对于一列“group_concat_max_len”,需要将变量设置为保存完整结果的大小。您可以在查询之前设置它,也可以直接在 my.cnf 中设置。

        SET  group_concat_max_len=16384;
        
        SELECT
          DATE(p.`time`) checkdate,
          DATE(SUBDATE(`p`.`TIME`, INTERVAL 31 DAY)) AS hist_start,
          DATE(SUBDATE(`p`.`TIME`, INTERVAL 1 DAY)) AS hist_end,
           sum(1) AS cnt_user,
          GROUP_CONCAT(DISTINCT p.user_id SEPARATOR ', ') user_not_hist
          FROM pageload_cum  p
          LEFT JOIN pageload_cum hist
            ON  `hist`.`TIME` BETWEEN DATE(SUBDATE(`p`.`TIME`, INTERVAL 31 DAY)) AND DATE(SUBDATE(`p`.`TIME`, INTERVAL 1 DAY))
            AND p.user_id = hist.user_id
          WHERE 
          hist.user_id IS NULL
         AND
           `p`.`TIME`  BETWEEN '2015-09-01 00:00:00' AND '2015-09-30 23:59:59' 
           GROUP BY DATE(`p`.`TIME`);
        

        结果

        我仅在页面加载表中使用 10.000.000 行测试了此查询,并为此结果更改了一些数据。

        +------------+------------+------------+----------+--------------------------+
        | checkdate  | hist_start | hist_end   | cnt_user | user_not_hist            |
        +------------+------------+------------+----------+--------------------------+
        | 2015-09-13 | 2015-08-13 | 2015-09-12 |        1 | 3333                     |
        | 2015-09-27 | 2015-08-27 | 2015-09-26 |        4 | 4567, 5678, 12345, 31313 |
        +------------+------------+------------+----------+--------------------------+
        2 rows in set (0.29 sec)
        

        最少的

        没有必要删除孔表。您只能删除您不再使用的日子里的旧数据,例如

        DELETE FROM pageload_cum WHERE `time` < DATE(SUBDATE(now(), INTERVAL 100 DAY));
        

        如果你想要报告,请现在告诉我。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-07-08
          • 2020-07-27
          • 2011-01-03
          • 2013-12-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-08-24
          相关资源
          最近更新 更多