4

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.

Chrissi
  • 87
  • 7
  • 1
    As the comment left on the FULL OUTER JOIN answer would suggest, it obviously didn't. Think about it - the WHERE clause filtering out the result of the JOIN effectively invalidates the result of the JOIN because those joined records would contain NULL values. The selection criteria needs to be applied on the actual JOIN. – nathanchere May 27 '10 at 03:29

3 Answers3

4

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
  • 811,555
  • 193
  • 1,581
  • 1,452
  • 1
    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 May 26 '10 at 06:02
  • 1
    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 May 26 '10 at 06:05
  • 1
    No, just a single year. I've updated the above question to reflect what happens when trying your answer, thanks. – Chrissi May 26 '10 at 06:13
  • 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 May 26 '10 at 06:17
  • 2
    @Chrissi: Are you sure that your recursive CTE is generating the correct results? Try `SELECT * FROM Periods` and see what happens. – Mark Byers May 26 '10 at 06:19
  • 1
    Your original answer worked after all, when I added sufficient JOIN ON clauses. – Chrissi May 26 '10 at 06:24
  • 1
    I've added the eventual answer to the end of the question. Thanks again. – Chrissi May 26 '10 at 06:28
2

I think what you need is LEFT OUTER JOIN

CoolBeans
  • 20,654
  • 10
  • 86
  • 101
MikeJiang
  • 65
  • 4
  • 1
    `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 May 26 '10 at 06:01
  • 2
    Sorry, it doesn't make any difference what JOIN variant I use as stated already. – Chrissi May 26 '10 at 06:03
  • 1
    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 May 26 '10 at 06:05
  • 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 May 26 '10 at 06:35
0

I think you are looking for a

FULL OUTER JOIN
because for every NULL "Year," its corresponding "TotalValue" is also NULL!
JohnB
  • 18,046
  • 16
  • 98
  • 110