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

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.

Related

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

how to get a Distinct Count of users from two related but different tables

Apologies for this but SQL is not a strong point for me, and whilst appears similar to lots of other queries I cannot translate those to this situation successfully.
I have two tables that will be related by a common value (id and Issue) if a row in table 2 exists.
I need to get a distinct count of users raising particular issues. I have users in both tables, with the table 2 user taking precedence if it exists.
There is always a REPORTER in Table 1, but there may not be a Stringvalue of Name (fieldtype = 1) in table 2. If there is a Stringvalue then that is the "User" and the Reporter can be ignored.
Table 1
| id | Reporter| Type |
| 1 | 111111 | 1 |
| 2 | 111111 | 2 |
| 3 | 222222 | 2 |
| 4 | 333333 | 1 |
| 5 | 111111 | 1 |
| 6 | 666666 | 1 |
Table 2
|issue | Stringvalue | fieldType|
| 1 | Fred | 1 |
| 1 | bananas | 2 |
| 2 | Jack | 1 |
| 5 | Steve | 1 |
I have a total of 4 issues of the right type (1,4,5,6), three reporters (111111,333333,666666) and two Stringvalues(Fred, Steve).
My total count of Distinct Users = 4 (Fred, 333333, Steve, 666666)
Result Table
| id| T1.Reporter | T2.Name |
| 1| Null | Fred |
| 4| 333333 | Null |
| 5| Null | Steve |
| 6| 666666 | Null |
How do I get this result in SQL!
Closest try so far:
SELECT
table1.REPORTER,
TO_CHAR(NULL) "NAME"
FROM table1
Where table1.TYPE =1
AND table1.REPORTER <> '111111'
Union
SELECT
TO_CHAR(NULL) "REPORTER",
table2.STRINGVALUE "NAME"
FROM table2,
table1
WHERE table2.ISSUE = table1.ID
AND table2.fieldtype= 1
and table1.issuetype = 1
Without explicitly excluding the default table 1 Reporter, this gets returned in my results even when there is a name value in table 2.
I have tried exists and in but cannot get syntax right or the correct results. As soon as try any Join that links the ID and Issue values the results always end up constrained to the matching rows or for all values. And added additional conditions to the ON does not return correct results.
I have tried too many permutations to list, logically this sounds like should be able to do union with where exists, or left outer join but my skills are lacking to make this work.
You need to use a LEFT JOIN and that is where you specify the fieldtype = 1 clause:
SELECT
table1.id,
CASE
WHEN table2.Stringvalue IS NOT NULL THEN table2.Stringvalue
ELSE table1.Reporter
END AS TheUser
FROM table1
LEFT JOIN table2 ON table1.id = table2.issue AND table2.fieldType = 1
WHERE table1.Type = 1
Result:
+------+---------+
| id | TheUser |
+------+---------+
| 1 | Fred |
| 4 | 333333 |
| 5 | Steve |
| 6 | 666666 |
+------+---------+
If I understand correctly, you want a left join and count(distinct). Here is what I think you are looking for:
select count(distinct coalesce(stringval, reporter) )
from table1 t1 left join
table2 t2
on t1.id = t2.issue and t2.fieldtype = 1
where t1.id in (1, 4, 5, 6);
You need to learn how to use explicit JOIN syntax. As a simple rule: Never use commas in the FROM clause. Always use explicit JOIN syntax. For one thing, it is more powerful, making it easy to express outer joins.

SQL Plus Query - Multiple Table Query

This is causing me more trouble then it should.
I have the following sample tables:
____________________ ____________
| Name| Number | | Number |
|_______|__________| |__________|
| Alice | 1 | | 1 |
| Bob | 2 | | 1 |
|_______|__________| |__________|
I want my result to be:
_____________________________________
| Name | Number | Count(Number) |
|________|___________|_______________|
| Alice | 1 | 2 |
| Bob | 2 | 0 |
|________|___________|_______________|
I'm going back and forward but I'm sure this shouldn't be so tricky. I assume I'm missing something.
I've modified Gordon's answer:
select name, number count(t2.number)
from table1 t1,
table2 t2
where t1.number = t2.number (+)
group by t1.number;
You need a join and aggregation. However, the join needs to be a left outer join to keep all the rows:
select name, number, count(t2.number)
from table1 t1 left outer join
table2 t2
on t1.number = t2.number
group by t1.name, t1.number;
And, the count() is counting the non-NULL values in the second table, so you can get 0 when there is no match.

Query for joining all info from three tables

I need to Join 3 tables in SQL Server. Those 3 tables have basically this schema:
Users Items UsersItems
+--------+ +--------+-------------+ +--------+--------+-------+
| UserID | | ItemID | Description | | UserID | ItemId | Value |
+--------+ +--------+-------------+ +--------+--------+-------+
| 1 | | 1 | desc1 | | 1 | 1 | 1 |
| 2 | | 2 | desc2 | | 1 | 2 | 2 |
| ... | | ... | desc3 | | 2 | 2 | 1 |
| n | | n | desc4 | | n | 1 | 1 |
+--------+ +--------+-------------+ +--------+--------+-------+
As you can see both Users and Items can grow indefinitely and UsersItems is used to express the relation between those two, also including a Value column.
I need a query to retrieve all users, and for each user I need all the items with it's corresponding Value.
If the relation doesn't exist in UsersItems then Null (or a default value) should be returned for that row's Value column.
The expected query result should be:
ResultSet
+--------+--------+-------+
| UserID | ItemID | Value |
+--------+--------+-------+
| 1 | 1 | 1 |
| 1 | 2 | 2 |
| 1 | n | NULL |
| 2 | 1 | NULL |
| 2 | 2 | 1 |
| 2 | n | NULL |
| n | 1 | 1 |
| n | n | NULL |
+--------+--------+-------+
Okay, since there are several answers that I think aren't correct, I'll post what I think the answer is:
SELECT Users.UserID,
Items.Description,
UsersItems.Value
FROM
Users
CROSS JOIN
Items
LEFT JOIN
UsersItems
ON
Users.UserID = UsersItems.UserID
AND
Items.ItemID = UsersItems.ItemID
I'm inferring from your comment about nulls that you want to see all Items againsts all Users, with the Value from the UsersItems table where it exists.
SELECT
Users.UserID,
Items.Description,
Items.Value
FROM Users LEFT OUTER JOIN UsersItems
ON Users.UserID = UsersItems.UserID
LEFT OUTER JOIN Items
ON UserItems.ItemID = Items.ItemID
You do it like this:
SELECT Users.UserID, Items.Description, Items.Value
FROM Users
LEFT OUTER JOIN UsersItems ON Users.UserID = UsersItems.UserID
LEFT OUTER JOIN Items ON UserItems.ItemID = Items.ItemID
For more info on left outer join read the following:
The result of a left outer join (or simply left join) for table A and
B always contains all records of the "left" table (A), even if the
join-condition does not find any matching record in the "right" table
(B). This means that if the ON clause matches 0 (zero) records in B,
the join will still return a row in the result—but with NULL in each
column from B. This means that a left outer join returns all the
values from the left table, plus matched values from the right table
(or NULL in case of no matching join predicate). If the right table
returns one row and the left table returns more than one matching row
for it, the values in the right table will be repeated for each
distinct row on the left table. From Oracle 9i onwards the LEFT OUTER
JOIN statement can be used as well as (+).

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