Most performant of Two SQL Double joins - sql

I have two tables that use the same compound key and I want to join them with a left join. Is my 1st query below the right way to do this, or would joining against B a second time as in my 2nd example be better/more performant? I should also mention that these tables exist on opposite sides of a database link, so I am thinking that less joins would be better...
Table A:
key1 | key2 | field
--------------------
1 | 1 | frog
1 | 2 | fish
3 | 2 | erik
Table B:
key1 | key2 | otherField
---------------------------
1 | 1 | "three times"
1 | 3 | "rarely"
2 | 2 | "once"
Desired Result:
key1 | key2 | field | otherField
---------------------------------
1 | 1 | frog | "three times"
1 | 2 | fish | null
3 | 2 | erik | null
Method 1:
select *
from A
left join B on (A.key1 = B.key1)
where (B.key2 = A.key2 or (B.key1 is null and B.key2 is null));
Method 2:
select *
from A
left join B b1 on (A.key1 = b1.key1)
left join B b2 on (A.key2 = b2.key2)

Simply left join on both keys:
SELECT A.key1, A.key2, A.field, B.otherfield
FROM A LEFT JOIN B
ON (A.key1 = B.key1)
AND (A.key2 = B.key2);

Related

difference between 'where' null and 'on' in a left join

Could someone explain to me why
select "talent".* from "talent"
left join "push" on "push"."talentId" = "talent"."id"
where ("push"."offerId" = '403' or "push"."offerId" is null)
yields less results than
select "talent".* from "talent"
left join "push" on "push"."talentId" = "talent"."id" and "push"."offerId" in ('403')
The way I see it, it should boil down to the same result, but it doesn’t, and I’m not sure what I miss to get it.
first one does not contain rows that have no entry in the push table.
I’d expect them to be caught by the or "push"."offerId" is null.
EDIT:
here is an example:
talent table
+----+------+
| id | name |
+----+------+
| 1 | John |
| 2 | Bob |
| 3 | Jack |
+----+------+
push table
+----+----------+---------+
| id | talentId | offerId |
+----+----------+---------+
| 1 | 1 | 403 |
| 2 | 1 | 42 |
| 3 | 2 | 123 |
| 3 | 2 | 456 |
+----+----------+---------+
With this data, the query with the where clause returns only
+----+------+---------+
| id | name | offerId |
+----+------+---------+
| 1 | John | 403 |
+----+------+---------+
while the one with the on condition returns all wanted rows
+----+------+---------+
| id | name | offerId |
+----+------+---------+
| 1 | John | 403 |
| 2 | Bob | null |
| 3 | Jack | null |
+----+------+---------+
The difference is when there is a match but on another row. This is best shown with a small example.
Consider:
t1:
x y
1 abc
1 def
2 xyz
t2:
x y
1 def
Then the left join version returns all three rows in t1:
select *
from t1 left join
t2
on t1.x = t2.x and t1.y = t2.y;
The filtering in the where clause version:
select *
from t1 left join
t2
on t1.x = t2.x
where t2.y = 'abc' or t2.y is null;
returns only one rows. The row that is returned is 1/abc. x = 2 matches in t2. So, t2.y is not null. And it is not 'abc' either. So it is filtered out.
Here is a db<>fiddle.
Yes, there is something you are missing.
WHERE and join conditions are only exchangeable for inner joins.
An outer join a LEFT JOIN b ON ... is defined as:
the result of the inner join
in addition, for every row in a that did not find a match that way, we get a result row where the b values are replaced with NULL.
So, no matter what the join condition is, the result will always contain at least one row for each value of a.
But a WHERE condition is evaluated (logically) after the join, so it can exclude rows from a from the query result.

select query joining two tables on a range

I have two tables:
Table A with columns
name | tag | price | ref
and Table B with columns:
id | time | min_ref | max_ref
I want to make the following query, take all columns from table A and columns id and time from Table B, combining rows in such a way that particular row from A is merged with a row from B if value ref from A is in the range (min_ref, max_ref). Example:
A
name | tag | price | ref
A | aaa | 78 | 456
B | bbb | 19 | 123
C | ccc | 5 | 789
B
id | time | min_ref | max_ref
0 | 26-01-2019 | 100 | 150
1 | 27-01-2019 | 450 | 525
2 | 25-01-2019 | 785 | 800
the query should return:
name | tag | price | ref | id | time
A | aaa | 78 | 456 | 1 | 27-01-2019
B | bbb | 19 | 123 | 0 | 26-01-2019
C | ccc | 5 | 789 | 2 | 25-01-2019
The notation (min_ref, max_ref) for ranges signifies exclusive bounds. Would be [min_ref, max_ref] for inclusive.
So:
select a.*, b.id, b.time
from a
join b on a.ref > b.min_ref
and a.ref < b.max_ref;
The BETWEEN predicate treats all bounds as inclusive.
I think this is just a join:
select a.*, b.id, b.time
from a join
b
on a.ref between b.min_ref and b.max_ref;
You want a JOIN which combines rows from the two tables with an appropriate criteria. For instance:
SELECT a.name, a.tag, a.price, a.ref, b.id, bi.time
FROM a
INNER JOIN b ON b.min_ref <= a.ref AND b.max_ref >= a.ref
The INNER JOIN finds matching rows from the two tables, ON a specified criteria. In this case, the criteria is that a.ref is between b.min_ref and b.max_ref.
You can also use the sql BETWEEN operator to simplify the conditionals:
SELECT ...
FROM a
INNER JOIN b ON a.ref BETWEEN b.min_ref AND b.max_ref

SQL JOIN two table & show all rows for table A

I have a question about JOIN.
TABLE A | TABLE B |
-----------------------------------------|
PK | div | PK | div | val |
-----------------------------------------|
A | a | 1 | a | 10 |
B | b | 2 | a | 100 |
C | c | 3 | c | 9 |
------------------| 4 | c | 99 |
-----------------------
There are two tables something like above, and I have been trying to join two tables but I want to see all rows from TABLE A.
Something like
SELECT T1.PK, T1.div, T2.val
FROM A T1
LEFT OUTER JOIN B T2
ON T1.div = T2.div
and I want the result would look like this below.
PK | div | val |
-------------------------
A | a | 10 |
A | a | 100 |
B | null | null |
C | c | 9 |
C | c | 99 |
I have tried all JOINs I know but B doesn't appear because it doesn't exist. Is it possible to show all rows on TABLE A and just show null if it doesn't exists on TABLE B?
Thanks in advance!
If you change your query to
SELECT T1.PK, T2.div, T2.val
FROM A T1
LEFT OUTER JOIN B T2
ON T1.div = T2.div
(Note, that div comes from T2 here.), you'll get exactly the result posted (but maybe in a different order, add an ORDER BY clause if you want a specific order).
Your query as it stands will get you:
PK | div | val |
-------------------------
A | a | 10 |
A | a | 100 |
B | b | null |
C | c | 9 |
C | c | 99 |
(Note, that div is b for the row with the PK of B, not null.)
To get to your resultset, all you need to do is use T2.Div as that is the value that does not exist in the second table:
SELECT T1.PK, T2.div, T2.val
FROM A T1
LEFT OUTER JOIN B T2
ON T1.div = T2.div

MSSQL-How to join a table and for those null values, specifically use one item from the joined tables?

Please check the tables below:
When table 1 left join Table 2 on Column ItemName, for Pear and Orange, the Coef column will be null. How can I get those non mapped items to all match for ItemName = Others in Table 2?
Currently, I am using temp table to filter those null items , put the correct coef and then union with the mapped item results to get the final Table 3.
Are there any better ways to achieve this?
Table 1:
ItemName | Cost |
Apple | 1 |
Banana | 2 |
Pear | 3 |
Orange | 4 |
Table 2:
ItemName | Coef |
Apple | 0.1 |
Banana | 0.2 |
Others | 0.8 |
Expected Results:
ItemName | Cost | Coef |
Apple | 1 | 0.1 |
Banana | 2 | 0.2 |
Pear | 3 | 0.8 |
Orange | 4 | 0.8 |
You could cross join your result with that single row and take the first non-null value:
SELECT t1.itemname, t1.cost, COALESCE(t2.coef, def.coef)
FROM t1
LEFT JOIN t2 ON t1.itemname = t2.itemname
CROSS JOIN (SELECT coef
FROM t2
WHERE itemname = 'Others') def
You can use two left joins:
select t1.*,
coalesce(t2.coef, t2o.coef) as coef
from t1 left join
t2
on t1.itemname = t2.itemname left join
t2 t2o
on t2o.itemname = 'Others';

Can I combine values from multiple rows in another table into multiple columns of one row using SQL?

I have two tables:
T1:
| M_ID | P_ID1 | P_ID2 | rest of T1 columns |
| 0 | 0 | 1 | ... |
| 1 | 2 | 3 | ... |
T2:
| P_ID | Type | A | B |
| 0 | 1 | a | e |
| 1 | 2 | b | f |
| 2 | 1 | c | g |
| 3 | 2 | d | h |
Now, I want to have a query that selects this:
| M_ID | P_1a | P_1b | P_2a | P_2b | rest of T1 columns |
| 0 | a | e | b | f | ... |
| 1 | c | g | c | h | ... |
So, in words: I want to select all columns from T1, but I want to replace P_ID1 with the columns from T2, where the P_ID is equal to P_ID1, and the type is 1, and basically the same for P_ID2.
I can obviously get the information I need with multiple queries, but I was wondering if there is a way that I can do this with one query. Any ideas?
I'm currently using SQL Server 2008r2, but I'd also be interested in solutions for other database software.
Thanks for the help!
Sure, you just need to use a join:
select T1.M_ID, t2_1.A as P_1a, t2_1.B as P_1b, t2_2.A as P_2a, t2_2.B as P_2b, ...
from T1, T2 t2_1, T2 t2_2
where T1.P_ID1 = t2_1.P_ID and T1.P_ID2 = t2_2.P_ID
basically we are joining T1 onto T2 twice, once for the P_1 values and a second time for the P_2 values. You need to alias T2 when you join it twice to distinguish between the two (that's what the t2_1 and t2_2 are - a means of distinguishing between the two instances of the joined T2).
This is the same as #John Pickup's solution only using modern join syntax:
select T1.M_ID, t2_1.A as P_1a, t2_1.B as P_1b, t2_2.A as P_2a, t2_2.B as P_2b, ...
from T1
join T2 t2_1 on T1.P_ID1 = t2_1.P_ID
join T2 t2_2 on T1.P_ID2 = t2_2.P_ID
I only post a seperate answer, as there is no code formatting in comments as you get told here