views:

55

answers:

3

I'm trying to write a quick (ha!) program to organise some of my financial information. What I ideally want is a query that will return all records with financial information in them from TableA. There should be one row for each month, but in instances where there were no transactions for a month there will be no record. I get results like this:


SELECT Period,Year,TotalValue FROM TableA WHERE Year='1997'

Result:

Period Year  TotalValue
1      1997  298.16
2      1997  435.25
4      1997  338.37
8      1997  336.07
9      1997  578.97
11     1997  361.23

By joining on a table (well a View in this instance) which just contains a field Period with values from 1 to 12, I expect to get something like this:


SELECT p.Period,a.Year,a.TotalValue
FROM Periods AS p
LEFT JOIN TableA AS a ON p.Period = a.Period
WHERE Year='1997'

Result:

Period Year  TotalValue
1      1997  298.16
2      1997  435.25
3      NULL  NULL
4      1997  338.37
5      NULL  NULL
6      NULL  NULL
7      NULL  NULL
8      1997  336.07
9      1997  578.97
10     NULL  NULL
11     1997  361.23
12     NULL  NULL

What I'm actually getting though is the same result no matter how I join it (except CROSS JOIN which goes nuts, but it's really not what I wanted anyway, it was just to see if different joins are even doing anything). That is, there are no NULL records, it only returns the records that exist with corresponding periods in TableA instead of 12 records from 1 to 12 regardless. LEFT JOIN, RIGHT JOIN, INNER JOIN all fail to provide the NULL records I am expecting.

Is there something obvious that I'm doing wrong in the JOIN? Does it matter that I'm joining onto a View?


Edit Using Mark Byers' example, I tried the following:

SELECT p.Period,a.Year,a.TotalValue
FROM Periods AS p
LEFT JOIN TableA AS a ON (p.Period = a.Period) AND (a.Year = '1997')

Result:

Period Year  TotalValue
1      1997  298.16
2      1997  435.25
4      1997  338.37
8      1997  336.07
9      1997  578.97
11     1997  361.23

It's effectively getting the same result in a different way, still not getting the expected NULL entries for 3,5,6,7,etc.


Many thanks to Mark Byers for helping come to the final solution, which for the record was:

SELECT p.Period, a.YEAR, SUM(a.Value) as TotalValue
FROM
    Periods as p
LEFT JOIN
    TableA as a
    ON d.Period = p.Period AND a.Year = '1997'
GROUP BY p.Period,a.Year,a.PERIOD
ORDER BY p.Period,a.Year;

In practice there is also a LedgerID field which is being grouped by, though the end result remains the same: filtering needs to be done on the JOIN, not on the result of the JOIN.

+4  A: 

This is wrong:

WHERE Year='1997'

You want rows where Year is 1997 or is NULL but the NULLs are being filtered away because of that WHERE clause. Use this instead:

LEFT JOIN TableA AS a
ON p.Period = a.Period
AND Year = '1997'

Note also that you don't need to persist the Periods table. You can also generate it dynamically using a recursive CTE. Your recursive CTE should look like this:

WITH Periods (Period) AS
(
    SELECT 1
    UNION ALL
    SELECT Period + 1 FROM Periods WHERE Period < 12
)
SELECT * FROM Periods
Mark Byers
@Mark: Great! :)
sza
That might still prove helpful but that's still not solving the main problem, sorry. Even when I take away the WHERE clause entirely, I still get a list of all the years but without any of the expected NULL records.
Chrissi
Chrissi: "Even when I take away the WHERE clause entirely" No - you must not do that. This type of query won't work for all years - only for *one specific year*. If you take away the extra clauses then you won't get the NULLs if you have just entry for each month in *any* year. If you want to get NULLs in *all* years then you need a completely different query. Is that what you want? If so, you should update your question to make that clear because it seems from your question that you are only interested in one year, which you *can* do using this method.
Mark Byers
No, just a single year. I've updated the above question to reflect what happens when trying your answer, thanks.
Chrissi
For the record, a recursive CTE is how I'm generating the Period view. It's just moved into a view for simplicity while I work out what's going on here.
Chrissi
@Chrissi: Are you sure that your recursive CTE is generating the correct results? Try `SELECT * FROM Periods` and see what happens.
Mark Byers
Your original answer worked after all, when I added sufficient JOIN ON clauses.
Chrissi
I've added the eventual answer to the end of the question. Thanks again.
Chrissi
+2  A: 

I think what you need is LEFT OUTER JOIN

MikeJiang
`LEFT JOIN` and `LEFT OUTER JOIN` are exactly the same. See http://stackoverflow.com/questions/406294/left-join-and-left-outer-join-in-sql-server
Mark Byers
Sorry, it doesn't make any difference what JOIN variant I use as stated already.
Chrissi
MikeJiang, think about it: how can you have a LEFT INNER JOIN or a RIGHT INNER JOIN? If you understand INNER JOINs, you'd realize it doesn't make sense. Therefore, you can use LEFT JOIN as shorthand for LEFT OUTER JOIN
JohnB
hi,this may work: SELECT p.Period,a.Year,a.TotalValue FROM Periods AS p LEFT JOIN (SELSECT * from TableA WHERE Year='1997' ) AS a ON p.Period = a.Period
MikeJiang
A: 

I think you are looking for a

FULL OUTER JOIN
because for every NULL "Year," its corresponding "TotalValue" is also NULL!

JohnB
That just produced NULLs for everything which didn't match the WHERE condition.
Chrissi