将COUNT与HAVING一起使用时有0个结果,但实际上存在行-MySQL

时间:2019-03-10 10:38:12

标签: mysql sql

我有一个表,要在其中计算每个域的记录

我有这样的查询。 Here is sqlfiddle表模式和查询

SELECT
    COUNT(*),
    SUBSTRING_INDEX( TRIM( LEADING 'www.' FROM TRIM( LEADING 'http://' FROM TRIM( LEADING 'https://' FROM link ) ) ), '/', 1 ) AS domain 
FROM
    links 
WHERE
    source = 'web' 
    AND DATE( last_seen ) = DATE( NOW( ) ) 
HAVING
    domain = 'testingwebsite.com' 

使用SELECT COUNT(*)时返回0个结果,但使用SELECT *时返回多个结果

我做错了什么?

3 个答案:

答案 0 :(得分:3)

您未按域分组

    SELECT
        COUNT(*),
        SUBSTRING_INDEX( TRIM( LEADING 'www.' FROM TRIM( LEADING 'http://' FROM TRIM( LEADING 'https://' FROM link ) ) ), '/', 1 ) AS domain 
    FROM
        links 
    WHERE
        source = 'web' 
        AND DATE( last_seen ) = DATE( NOW( ) ) 
     group  by  domain  
    HAVING
        domain = 'testingwebsite.com' 

如果您不添加group的列名,那么您将获得count(*)不可替代的值(在您的情况下,第一个由db engine ..遇到的值)

这种情况在mysql版本<5.7上发生(从mysql 5.7开始,不允许使用没有group by的聚合函数,也是为了避免这种情况=

答案 1 :(得分:2)

一种解决方案是添加GROUP BY。首先,您应该了解原因。

您有一个没有GROUP BY的聚合查询。 总是会产生一行。但是,未聚合的列(domain)将具有任意值。它可能与您比较中的域匹配,也可能不匹配。

您可以通过多种方式解决此问题。最有效的方法是省去domain中的SELECT,而只在WHERE中引用它:

SELECT COUNT(*)
FROM links l
WHERE source = 'web' AND
      last_seen >= CURDATE() AND -- probably no last_seen values in the future
      'testingwebsite.com' = SUBSTRING_INDEX( TRIM( LEADING 'www.' FROM TRIM( LEADING 'http://' FROM TRIM( LEADING 'https://' FROM link ) ) ), '/', 1 ) 

这保证返回一行,无论是否有任何行与WHERE子句匹配。如果没有行匹配,则计数将为0。我怀疑那是您想要的。

请注意,我也更改了日期比较。这样,查询就可以使用links(source, last_seen)上的索引。

最后,如果您确实希望在SELECT中使用该域,但又不想重复该域,则建议使用子查询:

SELECT domain, COUNT(*)
FROM (SELECT l.*,
             SUBSTRING_INDEX( TRIM( LEADING 'www.' FROM TRIM( LEADING 'http://' FROM TRIM( LEADING 'https://' FROM link ) ) ), '/', 1 ) as domain
      FROM links l
     ) l
WHERE source = 'web' AND
      last_seen >= CURDATE() AND -- probably no last_seen values in the future
      domain = 'testingwebsite.com'
GROUP BY domain;

请注意,如果数据中不存在域,则不会返回任何行。

关于效果的评论。此版本确实实现了子查询,这会产生开销(这是MySQL的缺点,但不是其他数据库的缺点)。但是,您的版本不仅实现了子查询,而且还聚合了所有数据,因此它应该比使用HAVING更快。通常,最好在聚合前 而不是事后进行过滤。

答案 2 :(得分:0)

您必须按域分组:

SELECT
    COUNT(*),
    SUBSTRING_INDEX( TRIM( LEADING 'www.' FROM TRIM( LEADING 'http://' FROM TRIM( LEADING 'https://' FROM link ) ) ), '/', 1 ) AS domain 
FROM
    links 
WHERE
    source = 'web' 
    AND DATE( last_seen ) = DATE( NOW( ) ) 
GROUP BY domain 

如果您想要特定域的结果,可以添加:

HAVING
    domain = 'testingwebsite.com' 

HAVING仅适用于SQL语句中的GROUP BY

  

HAVING子句必须在任何GROUP BY子句之后且在任何   ORDER BY子句

来自https://dev.mysql.com/doc/refman/8.0/en/select.html