Unsure what to use in query: Left Join - Inner Join - Where clause - sql

I have, let's say, two tables, employee and salary:
employee salary
|---------|---------| |--------|----------|----------|
| ID | Name | | E_ID | Amount | Status |
|-------------------| |------------------------------|
| 1 | Matt | | 1 | 100 | Past |
| 2 | John | | 1 | 120 | Current |
| 3 | David | | 2 | 150 | Current |
|---------|---------| |--------|----------|----------|
and I require a join between them that will return me the employee-salary info: null if it's not in the salary table, just one value if it's repeated (the one marked as 'current' in opposition to 'past').
In this example, my required return would be:
|---------|---------------|---------------|
| ID | Name | Amount |
|---------|---------------|---------------|
| 1 | Matt | 120 |
| 2 | John | 150 |
| 3 | David | NULL |
|---------|---------------|---------------|
I initially tried with a
WHERE employee.ID=salary.E_ID
but that would leave David out.
I tried a LEFT JOIN
...
employee
LEFT JOIN salary ON employee.ID=salary.E_ID
but that shows Matt twice, and if I add the where clause
...
employee
LEFT JOIN salary ON employee.ID=salary.E_ID
WHERE salary.status='Current'
that leaves David out again.
Not sure how to proceed.
Thank you for your help.

LEFT JOIN salary
ON employee.ID = salary.E_ID
AND salary.status = 'Current'

The given answer by Paparazzi is the way I'd do it too, but purely for information, another way to do this is is like this:
For your last example that ignores David again; if you use WHERE isnull(salary.status, 'Current') ='Current' instead like this:
...
employee
LEFT JOIN salary ON employee.ID=salary.E_ID
WHERE isnull(salary.status, 'Current')='Current'
Then, this will still include him, as we are saying, although the left join is not bringing back anything, treat the null as 'current' anyway, and match the where clause on this.

You should use the condition for salary.status in on clause
if you let this condition in where this work as a inner join and don't return the valye for all the employee
select employee.name, salary.amount
from employee
LEFT JOIN salary ON employee.ID=salary.E_ID AND salary.status='Current'

Related

SQL not returning values where null in another table

I'm trying to use the SQL join function to grab information from multiple tables.
My issue is I can't seem to get the desired result.
select a.DRINKER, sum(C.PRICE)
from DRINKERS a
left join ORDERS b on a.DRINKER = b.DRINKER
join SERVES c on b.PUB = c.PUB and d.DRINK = c.DRINK
group by a.DRINKER;
This gives the following results
----------------------
|DRINKER|sum(C.PRICE)|
----------------------
| BOB | 200.10 |
| NED | 172.50 |
| JOE | 270.90 |
| TIM | 80.10 |
----------------------
However I want this to be giving all of the people in a.DRINKER like such:
----------------------
|DRINKER|sum(C.PRICE)|
----------------------
| BOB | 200.10 |
| NED | 172.50 |
| JOE | 270.90 |
| TIM | 80.10 |
| PAT | null |
| ANN | null |
----------------------
Any guidance would be appreciated and if you could also explain the logic behind the changes that would be greatly appreciated as I wanna learn what I should be doing! Thanks in advance!
Even though you got a left join between DRINKERS and ORDERS, the join between ORDERS and SERVES, will filter out any nulls obtained in the first left join.
To fix this you could try by further left joining the tables
select a.DRINKER, sum(C.PRICE)
from DRINKERS a
left join ORDERS b on a.DRINKER = b.DRINKER
left join SERVES c on b.PUB = c.PUB and d.DRINK = c.DRINK
group by a.DRINKER;

*=* join? Is there such a thing? [duplicate]

This question already has answers here:
SQL JOIN and different types of JOINs
(6 answers)
Closed 3 years ago.
I need run a query that is like an left/right outer join. In other words I need all rows from both the left and right tables. But I don't need a cartesian product (cross join). I need to match on, in my case, email address. So given that, I have to output all rows from the left table, join the right table on email address, but all rows that do not match from either the left or right table need to be output as well with nulls for the fields from the opposite table. Sort of like a = join if there were such a thing, or left-righ outer join.
As for what I've tried: Google Searches. But didn't find anything. Cross apply might work, but I cannot wrap my brain around how that is any different from a join.
Example theoretical left-right join:
select users.*, contacts.*
from users
left-right join contacts on users.emailAddress = contacts.emailAddress
So if users contains:
----------------------------------
|emailAddress | firstName |
----------------------------------
|k#company.com | ken |
|b#enterprise.com | bill |
|j#establishment.com | joe |
----------------------------------
And contacts contains:
--------------------------------
|emailAddress | optedOut |
--------------------------------
|z#bigcompany.com | 0 |
|b#enterprise.com | 1 |
|h#smallcompany.com | 1 |
--------------------------------
The output should look like:
------------------------------------------------------------------
|emailAddress | firstName |emailAddress | optedOut |
------------------------------------------------------------------
|k#company.com | ken | NULL | NULL |
|b#enterprise.com | bill | b#enterprise.com | 1 |
|j#establishment.com | joe | NULL | NULL |
|NULL | NULL | z#bigcompany.com | 0 |
|NULL | NULL | h#smallcompany.com | 1 |
------------------------------------------------------------------
It's called a "full outer join". Your query should look like:
select users.*, contacts.*
from users
full outer join contacts on users.emailAddress = contacts.emailAddress

Simple SQL Query to bring back null if no match found

EDIT
I've edited this question to make it a little more concise, if you see my edit history you will see my effort and 'what I've tried' but it was adding a lot of unnecessary noise and causing confusion so here is a summary of input and output:
People:
ID | FullName
--------------------
1 | Jimmy
2 | John
3 | Becky
PeopleJobRequirements:
ID | PersonId | Title
--------------------
1 | 1 | Some Requirement
2 | 1 | Another Requirement
3 | 2 | Some Requirement
4 | 3 | Another Requirement
Output:
FullName | RequirementTitle
---------------------------
Jimmy | Some Requirement
Jimmy | Another Requirement
John | Some Requirement
John | null
Becky | null
Becky | Another Requirement
Each person has 2 records, because that's how many distinct requirements there are in the table (distinct based on 'Title').
Assume there is no third table - the 'PeopleJobRequirements' is unique to each person (one person to many requirements), but there will be duplicate Titles in there (some people have the same job requirements).
Sincere apologies for any confusion caused by the recent updates.
CROSS JOIN to get equal record for each person and LEFT JOIN for matching records.
Following query should work in your scenario
select p.Id, p.FullName,r.Title
FROM People p
cross join (select distinct title from PeopleJobRequirements ) pj
left join PeopleJobRequirements r on p.id=r.personid and pj.Title=r.Title
order by fullname
Online Demo
Output
+----+----------+---------------------+
| Id | FullName | Title |
+----+----------+---------------------+
| 3 | Becky | Another Requirement |
+----+----------+---------------------+
| 3 | Becky | NULL |
+----+----------+---------------------+
| 1 | Jimmy | Some Requirement |
+----+----------+---------------------+
| 1 | Jimmy | Another Requirement |
+----+----------+---------------------+
| 2 | John | NULL |
+----+----------+---------------------+
| 2 | John | Some Requirement |
+----+----------+---------------------+
use left join, no need any subquery
select p.*,jr.*,jrr.*
from People p left join
PeopleJobRequirements jr on p.Id=jrPersonId
left join JobRoleRequirements jrr p.id=jrr.PersonId
according the explanation, People and PeopleJobRequirements tables have many to many relationship (n to n).
so first of all you'll need another table to relate these to table.
first do this and then a full join will make it right.

considering using Cross Apply but not sure

This is my first question on stackoverflow and am looking forward to everyone's feedback and solutions.
I would put my current SQL skills at the lower end of intermediate.
Simple one for most of you: I need to write a query in an oracle SQL environment that returns all transactions after the active employees departure date.
Table looks like this:
| Name | dept | departure date |
| John | Sales | 3.12.2014 |
| David | IT | 7.27.2014 |
| Gary | Audit | 12.5.2013 |
Transaction table
| TransID | Emp Name | Amount | TransDate |
| 1 | John | 25.00 | 3.31.2014 |
| 2 | David | 30.00 | 8.13.204 |
| 3 | Gary | 15.00 | 1.1.2014 |
I'm trying to avoid a UNION ALL since the table has over 100+ employee records. On researching the use of CROSS APPLY it seemed like it could fit the situation. Any ideas are appreciated. Thanks!
Josh
You can just use a join:
select t.*
from employees e join
transactions t
on e.emp = t.emp and e.date < t.transdate;
You could write this using apply, but I think a join makes the intention more clear.

SQL LEFT JOIN help

My scenario: There are 3 tables for storing tv show information; season, episode and episode_translation.
My data: There are 3 seasons, with 3 episodes each one, but there is only translation for one episode.
My objetive: I want to get a list of all the seasons and episodes for a show. If there is a translation available in a specified language, show it, otherwise show null.
My attempt to get serie 1 information in language 1:
SELECT
season_number AS season,number AS episode,name
FROM
season NATURAL JOIN episode
NATURAL LEFT JOIN episode_trans
WHERE
id_serie=1 AND
id_lang=1
ORDER BY
season_number,number
result:
+--------+---------+--------------------------------+
| season | episode | name |
+--------+---------+--------------------------------+
| 3 | 3 | Episode translated into lang 1 |
+--------+---------+--------------------------------+
expected result
+-----------------+--------------------------------+
| season | episode| name |
+-----------------+--------------------------------+
| 1 | 1 | NULL |
| 1 | 2 | NULL |
| 1 | 3 | NULL |
| 2 | 1 | NULL |
| 2 | 2 | NULL |
| 2 | 3 | NULL |
| 3 | 1 | NULL |
| 3 | 2 | NULL |
| 3 | 3 | Episode translated into lang 1 |
+--------+--------+--------------------------------+
Full DB dump
http://pastebin.com/Y8yXNHrH
I tested the following on MySQL 4.1 - it returns your expected output:
SELECT s.season_number AS season,
e.number AS episode,
et.name
FROM SEASON s
JOIN EPISODE e ON e.id_season = s.id_season
LEFT JOIN EPISODE_TRANS et ON et.id_episode = e.id_episode
AND et.id_lang = 1
WHERE s.id_serie = 1
ORDER BY s.season_number, e.number
Generally, when you use ANSI-92 JOIN syntax you need to specify the join criteria in the ON clause. In MySQL, I know that not providing it for INNER JOINs results in a cross join -- a cartesian product.
LEFT JOIN episode_trans
ON episode_trans.id_episode = episode.id_episode
AND episode_trans.id_lang = 1
WHERE id_serie=1
You probably need to move the id_lang = 1 into the LEFT JOIN clause instead of the WHERE clause. Think of it this way... for all of those rows with no translation the LEFT JOIN gives you back NULLs for all of those translation columns. Then in the WHERE clause you are checking to see if that is equal to 1 - which of course evaluates to FALSE.
It would probably be easier if you included your code in the question next time instead of in a link.
Can you try using
LEFT OUTER JOIN
instead of
NATURAL LEFT JOIN