Left-Outer Join in Postgres Not Returning Values for Null

前端 未结 4 798
清歌不尽
清歌不尽 2021-01-04 13:07

A download is comprised of download-times, download-time id, and buno ID. Faults are comprised of fault-codes, download-time id, status, and type. A download can have many

相关标签:
4条回答
  • 2021-01-04 13:42

    The left outer join selects everything in the first table plus matching rows in the second table. The first table seems to consist of download attempts. So, your result from the "from" includes all download attempts.

    But, it does not necessarily contain all your fault codes. What is happening is that you have no faults for one or more codes that meet the criteria.

    You need a table that contains all the fault codes, in order for this to work. Here I just create a list of the fault codes as the first table. I think the following query does this:

    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 
    

    I admit: I am using SQL Server syntax to create "thefaults".

    0 讨论(0)
  • 2021-01-04 13:43

    This will require a RIGHT OUTER JOIN. The right outer join includes all values from the right table, with NULLs where there is no entry in the left table (I'm not sure if this will work with GROUP BY, though...) if fs_fault were a table with all fault codes.

    In your case, fs_fault seems to contain all faults for a download. Might this be the case for the unexpected behavior?

    0 讨论(0)
  • 2021-01-04 13:44

    If you want counts by faultcode, this seems like the simplest solution:

    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
    

    Note that I kept your conditions, where faults are not counted if they don't have the right statusid or bunoid. I was a bit afraid that the date selection might not have been doing what you thought, so I suggested an alternative. Even that might not do what you want if you're using TIMESTAMP WITHOUT TIME ZONE, but that's another story. I also added an ORDER BY clause, since you probably don't want the results in inconsistent order; without that clause it may or may not be in GROUP BY sequence, and that might change without warning.

    0 讨论(0)
  • 2021-01-04 13:50

    I'm giving my answer because I have significant doubts about the other answers. You gotta be careful about filter requirements. Remember, the where clause runs after your joins. So if there are any filter requirements in the where clause that refer to the non-outer joined table, you have (in many circumstances) nullified your outer join. So taking your sql, It seems the simplest solution is to either use the proper join or move the table names appropriately, and then move the filter conditions out of the where clause and into the join clause.

    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
    

    Another way which I believe should be equivalent is

    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
    

    As it doesn't strictly matter where the filter requirements on fs_fault are. (and your SQL engine's going to change that all up anyway).

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

    0 讨论(0)
提交回复
热议问题