MySQL,以SUM操作运行DISTINCT

时间:2018-07-04 13:38:51

标签: mysql sql sum

当前,我尝试根据用户性别计算应用程序中唯一身份用户访问的次数。这是计算所有访问次数(非唯一)的示例查询

SELECT
    DATE(v.visited_at) AS visit_date,
    SUM(IF(u.gender = 'M', 1, 0)) AS male_visit,
    SUM(IF(u.gender = 'F', 1, 0)) AS female_visit,
    SUM(IF(u.gender = '' OR u.gender IS NULL, 1, 0)) AS unknown_visit
FROM 
    visits v
    INNER JOIN users u ON v.user_id = u.id
WHERE
    DATE(v.visited_at) >= DATE_SUB(SYSDATE(), INTERVAL 30 DAY)
    AND v.duration > 30
GROUP BY
    DATE(v.visited_at)

尝试使用子查询并计算出与众不同的效果,但速度慢了4倍。

SELECT
    DATE(visited_at) as visit_date,
    (SELECT COUNT(DISTINCT u.id) FROM visits v JOIN users u ON v.user_id = u.id WHERE u.gender = 'M' AND DATE(v.visited_at) = visit_date AND v.duration > 30) AS male_visit,
    (SELECT COUNT(DISTINCT u.id) FROM visits v JOIN users u ON v.user_id = u.id WHERE u.gender = 'F' AND DATE(v.visited_at) = visit_date AND v.duration > 30) AS female_visit,
    (SELECT COUNT(DISTINCT u.id) FROM visits v JOIN users u ON v.user_id = u.id WHERE u.gender = '' OR u.gender IS NULL AND DATE(v.visited_at) = visit_date AND v.duration > 30) AS unknown_visit
FROM 
    visits v
WHERE
    DATE(visited_at) >= DATE_SUB(SYSDATE(), INTERVAL 30 DAY)
GROUP BY
    DATE(visited_at)

对此有何建议?

3 个答案:

答案 0 :(得分:1)

COUNT(DISTINCT)总是比COUNT()慢。您可以尝试:

SELECT DATE(v.visited_at) AS visit_date,
       COUNT(DISTINCT CASE WHEN u.gender = 'M' THEN u.id END) AS male_visit,
       COUNT(DISTINCT CASE WHEN u.gender = 'F' THEN u.id END) AS female_visit,
       COUNT(DISTINCT CASE WHEN u.gender = '' OR u.gender IS NULL THEN u.id END) AS unknown_visit
FROM visits v INNER JOIN
     users u
     ON v.user_id = u.id
WHERE DATE(v.visited_at) >= DATE_SUB(SYSDATE(), INTERVAL 30 DAY) AND
      v.duration > 30
GROUP BY DATE(v.visited_at);

不过,我不知道它会不会快得多。

答案 1 :(得分:0)

每个查询(用户和访问)都有2个表,其中包含示例数据。

user table

visit table

查询

:=

Result of Query

此查询将为您提供特定日期按性别划分的唯一用户数量。

答案 2 :(得分:0)

这种类型的查询可能会比较慢,尤其是当您在表中有大量条目时(例如,根据mysql必须执行全表扫描的日期和时间值选择行时)。

优化数据库结构可能会给您带来比以这种方式查询它要多得多的性能。

一些建议是按日期范围对表格进行分区。这样做可以大大减少查询的执行,因为这意味着与其进行全表扫描,mysql可以简单地忽略查询日期范围以外的任何分区。表格越大,您将看到的好处越多,但我期望的速度可能是2倍到10倍。

如果您要用3列malefemaleunknown替换“性别”列,则可以用单个查询替换包含慢COUNT(DISTINCT ...语句)的3个查询在条件较少的情况下,您还可以将用户ID添加到group by语句中,从而无需再进行计数,因为您可以为分组指定多个列。

最后,您可以添加数据库触发器,并且如果持续时间超过30并且是一天中的首次访问,则可以在记录访问时将其设置为1的额外列,或者您可以创建新的日历表来访问和使触发器在数据库写入每个日志时增加该值,该值等于当天的唯一访问量。