Postgres中的左外连接没有返回空值

时间:2012-05-14 20:02:32

标签: sql postgresql

下载包括下载时间,下载时间ID和buno ID。 故障由故障代码,下载时间ID,状态和类型组成。下载可能有许多错误,可以在下载时间ID上加入。

给定一组故障代码,结果必须包含每个具有相应故障计数的故障代码。如果在下载中未找到故障代码,则必须返回故障代码,故障计数为零。

问题似乎需要一个OUTER JOIN,但是在Postgres上没有看到这个工作正常,因为它似乎没有从LEFT表返回带有空值的集合。

查询如下,为简洁起见省略了一些细节:

SELECT  f.faultcode, f.downloadtimeid, d.downloadtime, count(*) as faultcount 
FROM    download_time d 
LEFT OUTER JOIN fs_fault f ON f.downloadtimeid = d.id
    AND f.faultcode IN (1000,1100)
    AND f.statusid IN(2, 4)
WHERE (d.downloadtime BETWEEN '04/11/2011' AND '05/01/2012')
    AND d.bunoid = 166501
GROUP BY d.bunoid, f.downloadtimeid, d.downloadtime, f.faultcode

第二天,我编辑了以显示答案。所有答案都很接近,并有各种各样的帮助。然而,JayC的答案是最接近的。 这是最终的SQL,只有作为WHERE子句进行故障代码IN语句的更改:

SELECT  f.faultcode, f.downloadtimeid, d.downloadtime, count(*) as faultcount
FROM    download_time d  
RIGHT OUTER JOIN fs_fault f ON f.downloadtimeid = d.id
        AND f.statusid IN(2, 4)
        AND d.downloadtime BETWEEN '04/11/2011' AND '05/01/2012'
        AND d.bunoid = 166501
WHERE f.faultcode IN (1000,1100)
GROUP BY d.bunoid, f.downloadtimeid, d.downloadtime, f.faultcode

谢谢,谢谢你的帮助!喜欢这个网站!

4 个答案:

答案 0 :(得分:22)

我正在给出答案,因为我对其他答案有很大疑问。你必须小心过滤器的要求。请记住,where子句在您加入后运行。因此,如果where子句中有任何过滤器要求引用非外连接表,则您(在许多情况下)使外连接无效。所以拿你的sql,似乎最简单的解决方案是使用正确的连接或适当地移动表名,然后将过滤条件移出where子句并进入join子句。

SELECT  f.faultcode, f.downloadtimeid, d.downloadtime, count(*) as faultcount 
FROM    download_time d 
RIGHT OUTER JOIN fs_fault f ON 
    f.downloadtimeid = d.id
    AND f.faultcode IN (1000,1100)
    AND f.statusid IN(2, 4)
    AND d.downloadtime BETWEEN '04/11/2011' AND '05/01/2012')
    AND d.bunoid = 166501
GROUP BY d.bunoid, f.downloadtimeid, d.downloadtime, f.faultcode

我认为应该相同的另一种方式是

SELECT  f.faultcode, f.downloadtimeid, d.downloadtime, count(*) as faultcount 
FROM    download_time d 
RIGHT OUTER JOIN fs_fault f ON 
    f.downloadtimeid = d.id
    AND d.downloadtime BETWEEN '04/11/2011' AND '05/01/2012')
    AND d.bunoid = 166501
WHERE
    f.faultcode IN (1000,1100)
    AND f.statusid IN(2, 4)
GROUP BY d.bunoid, f.downloadtimeid, d.downloadtime, f.faultcode

因为对fs_fault的过滤要求不严格。 (而且你的SQL引擎无论如何都会改变这一点。)

编辑:这是SQLFiddle演示对join子句与where子句的过滤。

答案 1 :(得分:2)

这需要RIGHT OUTER JOIN。右外连接包括右表中的所有值,其中NULL s左表中没有条目(我不确定这是否适用于GROUP BY,尽管......) 如果 fs_fault是包含所有错误代码的表格。

在您的情况下,fs_fault似乎包含下载的所有错误。这可能是意外行为的原因吗?

答案 2 :(得分:2)

如果你想通过faultcode计算,这似乎是最简单的解决方案:

WITH fc(faultcode) AS (VALUES (1000,1100))
SELECT fc.faultcode, count(d.downloadtimeid) as faultcount 
  FROM fc
  LEFT JOIN (fs_fault f ON f.faultcode = fc.faultcode
                       AND f.statusid IN(2, 4)
  JOIN download_time d ON d.id = f.downloadtimeid
                      AND d.bunoid = 166501
                      AND d.downloadtime::date BETWEEN date '2011-04-11'
                                                   AND date '2011-05-01')
  GROUP BY fc.faultcode
  ORDER BY fc.faultcode

请注意,我保留了您的条件,如果他们没有正确的statusid或bunoid,则不会计算错误。我有点害怕日期选择可能没有按照你的想法进行,所以我提出了另一种选择。如果您使用TIMESTAMP WITHOUT TIME ZONE,即使这可能不会达到您想要的效果,但这是另一个故事。我还添加了一个ORDER BY子句,因为您可能不希望结果的顺序不一致;如果没有该条款,它可能会或可能不会处于GROUP BY序列中,并且可能会在没有警告的情况下发生变化。

答案 3 :(得分:1)

左外连接选择第一个表中的所有内容以及第二个表中的匹配行。第一个表似乎包含下载尝试。因此,“来自”的结果包括所有下载尝试。

但是,它不一定包含所有的错误代码。发生的事情是,您对一个或多个符合标准的代码没有任何错误。

您需要一个包含所有故障代码的表,以便使其正常工作。这里我只是创建一个故障代码列表作为第一个表。我认为以下查询执行此操作:

SELECT thefaults.faultcode, f.downloadtimeid, d.downloadtime, count(*) as faultcount
FROM  (select 1000 as faultcode union all select 1100
      ) thefaults join
      fs_fault f
      on f.faultcode = thefaults.faultcode and
         f.statusid in (2, 4) left outer join
      download_time d
      ON f.downloadtimeid = d.id
WHERE (d.downloadtime BETWEEN '04/11/2011' AND '05/01/2012') AND
      d.bunoid = 166501
GROUP BY d.bunoid, f.downloadtimeid, d.downloadtime, f.faultcode 

我承认:我使用SQL Server语法来创建“thefaults”。