查询来自同一个表的记录在过去30天内不存在

时间:2015-10-02 06:07:51

标签: mysql sql

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

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'

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

请有人帮助我。

3 个答案:

答案 0 :(得分:0)

尝试使用

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的索引也可以使用。

答案 1 :(得分:0)

如果你有子查询,

IN子句非常慢。更好地使用临时表和连接,它的速度要快得多。

答案 2 :(得分:0)

我做了一些测试,找到了最好的解决方案。我现在没有多久会生成这个报告,但如果你使用新的累积表我会得到最好的结果。

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

新表格

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));

如果您想要报告,请立即告诉我。