Trouble with left outer join and multiple conditions - sql

I want to left outer join and display all id records that appear in table1 even if they don't exist in the table2. Table1 can have multiple id records for a year so I need to select distinct.
Here is the selection of ID's i need from table1
select distinct a.id
from table1 a
where a.year = 2022
and a.type = 'R'
This will display 650 records
So here is my attempt to join with another table. I want to still see the all the results of the first query on table1 even if there is no record found in table 2. So the desired result should be 650 records in total.
select distinct a.id, b.value
from table1 a left join table2 b on
a.id = b.id
where b.month = 'JAN'
and a.year = 2022
and a.type = 'R';
My result gives me 550 records. So I am still missing 100 records which do not exist in table2 that need to be included in the query.
If I change the WHERE to an AND in the ON condition, I will get all YEARS displayed and all TYPES. Resulting in a massive amount of records.
select distinct a.id, b.value
from table1 a left join table2 b on
a.id = b.id
and b.month = 'JAN'
and a.year = 2022
and a.type = 'R';
What am I missing? I though left joins will display all records in one table even if they don't exist in another. I don't really understand how to do this will multiple conditions.

What am I missing? I though left joins will display all records in one table even if they don't exist in another. I don't really understand how to do this will multiple conditions.
A LEFT OUTER JOIN condition will display all records from the table on the left of the join and any matching ones on the right of the join or NULL values otherwise.
The conditions in the WHERE clause will only display records that are true.
You are putting the b.month = 'JAN' condition into the WHERE clause (and not into the JOIN condition) so it will look for only those rows where the WHERE clause it true and then it will not match any rows where b.month is NULL and you have effectively turned the join condition to an INNER JOIN and not a LEFT OUTER JOIN.
You have several choices:
Perform the filtering in the WHERE clause but do it in a sub-query before joining the tables:
SELECT a.id, b.value
FROM (
select distinct
id
from table1
where year = 2022
and type = 'R'
) a
LEFT OUTER JOIN (
select id, value
from table2
where month = 'JAN'
) b
ON (a.id = b.id)
Put the b.month = 'JAN' filter into the JOIN condiiton:
SELECT DISTINCT
a.id, b.value
FROM table1 a
LEFT OUTER JOIN table2 b
ON (a.id = b.id AND b.month = 'JAN')
WHERE year = 2022
AND type = 'R';

How about
select distinct a.id, b.value
from table1 a left join table2 b on a.id = b.id and b.month = 'JAN'
where a.year = 2022
and a.type = 'R';

Related

When I do left join, I am still not able to get data from left table?

The condition is I know the right table does not have all matching records with the left table.
but I am still not able to get data from left table with null from right table
select a.sales, b.profit
from T1 a
left join T2 b on a.id = b.id
where b.category = 'office'
and b.code = '245'
because of the where condition of right table, the right table does not have matching records,
without where condition I got the records.
My question is will left table be affected with where condition of right table although using left join to retain the left table records.
Your WHERE clause forces the query to only return rows which b.category and b.code match the required values and so are non-NULL; this effectively turns your JOIN condition into an INNER JOIN.
You want to put the filters in the join condition:
select a.sales,
b.profit
from T1 a
left join T2 b
on ( a.id = b.id
AND b.category = 'office'
AND b.code = '245')
Or to pre-filter T2 in a sub-query:
select a.sales,
b.profit
from T1 a
left join (
SELECT *
FROM T2
WHERE category = 'office'
AND code = '245'
) b
on a.id = b.id

using where clause with Union

I have two tables, t1 and t2, with identical columns(id, desc) and data. But one of the columns, desc, might have different data for the same primary key, id.
I want to select all those rows from these two tables such that t1.desc != t2.desc
select a.id, b.desc
FROM (SELECT * FROM t1 AS a
UNION ALL
SELECT * FROM t2 AS b)
WHERE a.desc != b.desc
For example, if t1 has (1,'aaa') and (2,'bbb') and t2 has(1,'aaa') and (2,'bbb1') then the new table should have (2,'bbb') and (2,'bbb1')
However, this does not seem to work. Please let me know where I am going wrong and what is the right way to do it right.
Union is not going to compare the data.You need Join here
SELECT *
FROM t1 AS a
inner join t2 AS b
on a.id =b.id
and a.desc != b.desc
UNION ALL dumps all rows of the second part of the query after the rows produced by the first part of the query. You cannot compare a's fields to b's, because they belong to different rows.
What you are probably trying to do is locating records of t1 with ids matching these of t2, but different description. This can be achieved by a JOIN:
SELECT a.id, b.desc
FROM t1 AS a
JOIN t2 AS b ON a.id = b.id
WHERE a.desc != b.desc
This way records of t1 with IDs matching records of t2 would end up on the same row of joined data, allowing you to do the comparison of descriptions for inequality.
I want both the rows to be selected is the descriptions are not equal
You can use UNION ALL between two sets of rows obtained through join, with tables switching places, like this:
SELECT a.id, b.desc -- t1 is a, t2 is b
FROM t1 AS a
JOIN t2 AS b ON a.id = b.id
WHERE a.desc != b.desc
UNION ALL
SELECT a.id, b.desc -- t1 is b, t2 is a
FROM t2 AS a
JOIN t1 AS b ON a.id = b.id
WHERE a.desc != b.desc
The UNION operator is used to combine the result-set of two or more SELECT statements.
Notice that each SELECT statement within the UNION must have the same number of columns. The columns must also have similar data types.
So, if it has same number of columns and same datatype, then use Union otherwise join only Can be used.
SELECT *
FROM t1 AS a
inner join t2 AS b
on a.id =b.id
and a.desc != b.desc

Compare 2 fields in different rows of the same table

I want to return results from a join where the birth_date of two+ records are the same, but the person_id's are not equal. I am using Oracle. So if I got 4 results where rows 1 & 2 have the same birth_date and different person_id, those rows would be returned. Where rows 3 & 4 have the same birth_date and same person_id, those rows would not be returned. I get results, but I want to filter our results where the birth_date of rows are equal, but the person_id is <>.
select t3.field1, t6.field2, t6.field3, t3.field4, t3.field5
from table1 t1
inner join table2 t2 on t1.#matching = t2.#matching
inner join table3 t3 on t3.#matching = t1.#matching
inner join table4 t4 on t4.#matching = t1.#matching
inner join table5 t5 on t5.#matching = t4.#matching
inner join table6 t6 on t6.#matching = t3.#matching
where t1.#requirement = 'xxx'
and t2.#requirement = 'xxx'
and t2.#requirement is null
and t4.#requirement = 'xxx'
and t5.#requirement = 'xxx'
and t1.#requirement ='xxx'
and t5.#requirement is null
order by t1.#field ASC;
SELECT a.birth_date, a.id, b.id
FROM some_table a, some_table b
WHERE a.birth_date = b.birth_date
AND a.id < b.id
Note the usage of < instead of the intuitive !=. This is done to prevent the same combination returning in different orders (e.g. (1,2) and (2,1)).
Have you tried to group your data by person_id.. This will put all the records with the same person_id into 1 record
The query would look something like this
SELECT a.birth_date, a.id, b.id
FROM some_table a, some_table b
WHERE a.birth_date = b.birth_date
AND a.id < b.id
GROUP BY a.id

sql, outer join

I have two tables, linked with an outer join. The relationship between the primary and secondary table is a 1 to [0..n]. The secondary table includes a timestamp column indicating when the record was added. I only want to retrieve the most recent record of the secondary table for each row in the primary. I have to use a group by on the primary table due to other tables also part of the SELECT. There's no way to use a 'having' clause though since this secondary table is not part of the group.
How can I do this without doing multiple queries?
For performance, try to touch the table least times
Option 1, OUTER APPLY
SELECT *
FROM
table1 a
OUTER APPY
(SELECT TOP 1 TimeStamp FROM table2 b
WHERE a.somekey = b.somekey ORDER BY TimeStamp DESC) x
Option 2, Aggregate
SELECT *
FROM
table1 a
LEFT JOIN
(SELECT MAX(TimeStamp) AS maxTs, somekey FROM table2
GROUP BY somekey) x ON a.somekey = x.somekey
Note: each table is mentioned once, no correlated subqueries
Something like:
SELECT a.id, b.*
FROM table1 a
INNER JOIN table2 b ON b.parentid = a.id
WHERE b.timestamp = (SELECT MAX(timestamp) FROM table2 c WHERE c.parentid = a.id)
Use LEFT JOIN instead of INNER JOIN if you want to show rows for IDs in table1 without any matches in table2.
select *
from table1 left outer join table2 a on
table1.id = a.table1_id
where
not exists (select 1 from table2 b where a.table1_id = b.table1_id and b.timestamp > a.timestamp)
The quickest way I know of is this:
SELECT
A.*,
B.SomeField
FROM
Table1 A
INNER JOIN (
SELECT
B1.A_ID,
B1.SomeField
FROM
Table2 B1
LEFT JOIN Table2 B2 ON (B1.A_ID=B2.A_ID) AND (B1.TimeStmp < B2.TimeStmp)
WHERE
B2.A_ID IS NULL
) B ON B.A_ID = A.ID

Semantic difference between join queries

I have two queries that I thought meant the same thing, but I keep getting different results and I was hoping someone could explain how these are different:
1.
select *
from table1 a
left join table2 b on a.Id = b.Id and a.val = 0
where b.Id is null
2.
select *
from table1 a
left join table2 b on a.Id = b.Id
where b.Id is null
and a.val = 0
The point of the query is to find the rows that are in table1 and val = 0 that are not in table2.
I'm using sql server 2008 as well, but I doubt that this should matter.
When considering left joins think of them as having 3 conceptual stages.
The join filter is applied
The left rows are added back in
the where clause is applied.
You will then see why you get different results.
That also explains why this returns results
select o.*
from sys.objects o
left join sys.objects o2 on o.object_id=o2.object_id and 1=0
And this doesn't.
select o.*
from sys.objects o
left join sys.objects o2 on o.object_id=o2.object_id
where 1=0
SELECT * from TABLE1 t1
WHERE Val = 0
AND NOT EXISTS(SELEct 1 from Table2 t2 Where t1.Id = t2.Id)
If you remove the WHERE clause entirely, using a LEFT OUTER JOIN means that all the rows from the table on the left hand side will appear, even if they don't satisfy the JOIN criteria. For example, no rows satisfy the expression 1 = 0 however this:
SELECT *
FROM table1 AS a
LEFT OUTER JOIN table2 AS b
ON a.Id = b.Id
AND 1 = 0;
still results in all rows in table1 being returned where the id values match. Simply put, that's the way OUTER JOINs work.
The WHERE clause is applied after the JOIN, therefore this
SELECT *
FROM table1 AS a
LEFT OUTER JOIN table2 AS b
ON a.Id = b.Id
WHERE 1 = 0;
will return no rows.