【问题标题】:Left-Outer Join in Postgres Not Returning Values for NullPostgres中的左外连接不返回空值
【发布时间】:2012-05-14 20:02:32
【问题描述】:

下载由下载时间、下载时间 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

谢谢大家的帮助!喜欢这个网站!

【问题讨论】:

  • 您分析过您的数据吗?我怀疑这是 PostgreSQL 中的问题……您能提供SQL Fiddle 中的表结构和示例数据吗?
  • download_time d LEFT OUTER JOIN fs_fault f ON f.downloadtimeid = d.iddownload_time 作为左表,而不是fs_fault。联接条件与联接中的左表或右表无关。

标签: sql postgresql


【解决方案1】:

我给出我的答案是因为我对其他答案有很大的怀疑。你必须小心过滤器的要求。请记住,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 子句的过滤。

【讨论】:

  • 您对WHERE 子句与JOIN 条件的看法是对的。我在这方面确定了我的答案。不过,我认为您还没有解决他关于如何让所需的故障代码显示即使没有发生的问题。
  • 谢谢大家:我为所有受访者投了票,因为我从每个人身上学到了一些东西。然而,JayC,你的最接近。唯一的变化是将“f.faultcode IN (1000,1100)”移动到 where 子句。这样做会显示正确的故障代码。谢谢,JayC!
  • 我无法让 SQLFiddle 工作。我正在使用 IE 7.0.5730.13CO,因为我在工作。我必须在我通常运行 Chrome 的家里尝试一下。
【解决方案2】:

这需要RIGHT OUTER JOIN。右外连接包括右表中的所有值,其中NULLs 左表中没有条目(我不确定这是否适用于GROUP BY,不过......)如果 fs_fault 是一个包含所有故障代码的表格。

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

【讨论】:

    【解决方案3】:

    如果您想按故障代码计数,这似乎是最简单的解决方案:

    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 序列中,并且可能会在没有警告的情况下更改。

    【讨论】:

    • 如果故障码没有关联的下载时间,会被where子句过滤掉。
    • 糟糕。通过添加括号来强制连接的评估顺序进行修复。谢谢!
    • @JayC 在他的回答中对WHERE 子句与JOIN 条件提出了一个很好的观点。如前所述,如果条件排除了特定故障代码的 only 行,则故障代码将不会出现。通过移动条件来修复。
    【解决方案4】:

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

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

    您需要一个包含所有故障代码的表,才能使其正常工作。在这里,我只是创建了一个故障代码列表作为第一个表。我认为以下查询可以做到这一点:

    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 语法来制造“错误”。

    【讨论】:

    • 如果故障码没有关联的下载时间,会被where子句过滤掉。
    • 人们认为这就是提问者想要的。
    猜你喜欢
    • 1970-01-01
    • 2011-07-22
    • 1970-01-01
    • 1970-01-01
    • 2018-11-26
    • 2010-12-12
    • 2012-03-20
    • 1970-01-01
    • 2017-09-28
    相关资源
    最近更新 更多