为什么IFNULL不能从左连接移植?

时间:2012-09-13 17:44:40

标签: mysql

基本上我想要一个列,如果为null则返回0。我在嵌套语句中使用ifnull,当我自己运行嵌套语句时,列返回0,但是当我运行整个查询时,我再次得到NULL。这是我的代码:

SELECT Text, Verbo, Objeto, mPosts FROM Posts
    LEFT JOIN (SELECT ID, IFNULL(COUNT(*),0) AS mPosts FROM `Posts` WHERE Date >= DATE_ADD(CURDATE(), INTERVAL -30 DAY) GROUP BY `Verbo`,`Objeto`) AS B ON Posts.ID = B.ID

我可以将IFNULL移动到主要的选择语句,但我宁愿不这样做:

SELECT Text, Verbo, Objeto, IFNULL(mPosts,0) FROM Posts
    LEFT JOIN (SELECT ID, COUNT(*) AS mPosts FROM `Posts` WHERE Date >= DATE_ADD(CURDATE(), INTERVAL -30 DAY) GROUP BY `Verbo`,`Objeto`) AS B ON Posts.ID = B.ID

为什么我不能在嵌套语句中执行此操作?它可以以任何其他方式完成,以便我不必在主查询中使用它吗?

2 个答案:

答案 0 :(得分:1)

如果您NULL收到IFNULL(COUNT(*),0),那是因为LEFT JOINON的{​​{1}}条款不匹配,所以右表中的所有值均为Posts.ID = B.ID

是的,您需要将NULL移至顶级IFNULL子句。

答案 1 :(得分:0)

您必须在外部查询中拥有IFNULL

内部查询中的COUNT(*)没有返回NULL,当左侧的行没有来自右侧行源的匹配行时,LEFT JOIN操作正在创建NULL侧。

此查询与您的查询相同:

SELECT a.Text
     , a.Verbo
     , a.Objeto
     , c.mPosts
  FROM `Posts` a
  LEFT
  JOIN ( SELECT b.ID
              , COUNT(*) AS mPosts
           FROM `Posts` b
          WHERE b.Date >= DATE_ADD(CURDATE(), INTERVAL -30 DAY)
          GROUP BY b.`Verbo`, b.`Objeto`
       ) c
    ON c.ID = a.ID

重新格式化的查询可以更明显地解决问题所在。

您的内联视图(子查询)正在两列上执行GROUP BY,然后从每个计数值中包含的某一行返回ID的值。

(其他关系数据库会在这个语句中抛出“非聚合在选择列表中,不在分组中”类型异常,其中MySQL更自由,这既方便又诅咒。)

查询返回的结果集很奇怪。目前还不清楚你想要在这里返回什么结果集。

“count”子查询中的行将与每个COUNT()值中包含的一行匹配。 (只要COUNT(*)大于1,外部查询中就会有一行没有来自内联视图的匹配行... GROUP BY正在折叠所有的ID值包含的行只包含一行中包含的某个ID值。

我毫不犹豫地提出任何建议,因为我不清楚你想要归还什么。

你可以做的一件事是从WHERE子句中删除谓词,而是在聚合中检查这个条件,就像这样......

SELECT a.`Text`
     , a.`Verbo`
     , a.`Objeto`
     , c.`mPosts`
  FROM `Posts` a
  LEFT
  JOIN ( SELECT b.ID
              , SUM(IF b.`Date` >= DATE_ADD(CURDATE(), INTERVAL -30 DAY)) AS `mPosts`
           FROM `Posts` b
          GROUP BY b.`Verbo`, b.`Objeto`
       ) c
    ON c.ID = a.ID

这可能会减少NULL值的数量,但是它没有解决LEFT JOIN的更基本的问题,左边的任何一行都没有从右侧产生匹配,从而产生NULL。