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
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".
This will require a RIGHT OUTER JOIN
. The right outer join includes all values from the right table, with NULL
s 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?
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.
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.