10

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

4 回答 4

23

我给出我的答案是因为我对其他答案有很大的怀疑。你必须小心过滤器的要求。请记住, where 子句在您的 joins 之后运行。因此,如果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 引擎无论如何都会改变这一切)。

Edit: Here's a SQLFiddle demonstrating filtering on the join clause vs. the where clause.

于 2012-05-15T00:30:04.897 回答
2

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

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

于 2012-05-14T20:11:34.977 回答
2

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

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顺序排列,并且可能会在没有警告的情况下发生变化。

于 2012-05-14T20:57:27.933 回答
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 语法来创建“错误”。

于 2012-05-14T20:19:09.280 回答