Reputation: 39
I have 2 separate SQL queries and I can't figure out how they are different:
SELECT A.AccountName,
T.Total
FROM ACCOUNTS A
LEFT OUTER JOIN (SELECT *
FROM Totals
WHERE PersonID = @PersonID
AND Yr = @Year) T
ON A.AccountID = T.AccountID
ORDER BY AccountName
SELECT A.AccountName,
T.Total
FROM ACCOUNTS A
LEFT OUTER JOIN (SELECT *
FROM Totals) T
ON A.AccountID = T.AccountID
WHERE PersonID = @PersonID
AND Yr = @Year
ORDER BY AccountName
Any help would be greatly appreciated.
Upvotes: 1
Views: 121
Reputation: 6712
Whenever you put simple clauses for an OUTER JOIN table, you have to test if the the outer table is present before testing the condition, or else, in practical terms, you transformed the outer join to an inner join. In you case:
-- original
SELECT A.AccountName, T.Total
FROM ACCOUNTS A
LEFT OUTER JOIN (SELECT *
FROM Totals
WHERE PersonID = @PersonID
AND Yr = @Year
) T ON A.AccountID = T.AccountID
ORDER BY AccountName;
-- modified with same result
SELECT A.AccountName, T.Total
FROM ACCOUNTS A
LEFT OUTER JOIN Totals T ON A.AccountID = T.AccountID
WHERE (1=2 -- syntactic sugar
OR (T.AccountID IS NULL) -- this prevents outer joined rows to be excluded
OR (T.PersonID = @PersonID AND T.Yr = @Year) -- original filter
)
ORDER BY AccountName;
Upvotes: 0
Reputation: 23829
By applying the WHERE
conditions after the outer join (the second query), you are potentially eliminating records from the result set. The first query will include every line from the ACCOUNTS
table.
That is, in the first query, the returned table will have at least a row for every record in the ACCOUNTS
table, with NULL
s where there is no matching data in the Totals
table. If there are multiple matches in the Totals
table, then you will get multiple rows for that ACCOUNT
.
The second query will only return rows in ACCOUNTS
that match the parameters, and they must have a matching Totals
row.
Upvotes: 1
Reputation: 416179
The first query will only show a result for all Account records, whether or not an Account has a Total record that matches your @Year and @Person values (or even whether nor not it matches a Total record at all). It will have a NULL for the Total column for records that do not match, but there will be a result for all accounts. The second query will show only Accounts that have a total that matches the year and person. If there is no match, the Account record will not appear in your results at all.
And the 2 queries should really be written more like this (no need for subqueries here):
SELECT A.AccountName, T.Total
FROM ACCOUNTS A
LEFT JOIN Totals T
ON A.AccountID = T.AccountID AND T.PersonID = @PersonID AND T.Yr = @Year
ORDER BY AccountName
SELECT A.AccountName, T.Total
FROM ACCOUNTS A
LEFT JOIN Totals T ON A.AccountID = T.AccountID
WHERE T.PersonID = @PersonID AND T.Yr = @Year
ORDER BY AccountName
The above code is functionally equivalent to your originals.
Finally, it sounds like you may want something more like this:
SELECT A.AccountName, T.Total
FROM Accounts A
INNER JOIN Totals T ON T.AccountID = A.AccountID
WHERE T.PersonID = @PersonID AND T.Yr = @Year
In this query, it does not matter where you put your Year and Person conditions. The query will have the same result either way: an account record will only be shown if it matches a Totals record with the desired Year and Person. Accounts that don't match won't appear in the results at all.
Upvotes: 1