LEFT Join and data in right table - sql

I have a problem with my sql join request, I need to get lines of left table who are not referenced in right table or referenced in right table with status equal to 0 for my user 1.
I also need the field status of right table
Here is my two tables :
Table left
ID | title
1 | Title 1
2 | Title 2
3 | Title 3
4 | Title 4
5 | Title 5
Table right
ID | status | user | left_id
1 | 0 | 1 | 1
2 | 0 | 50 | 1
3 | 1 | 1 | 2
4 | 0 | 50 | 2
5 | 0 | 1 | 3
6 | 1 | 50 | 3
My goal is to get this result :
left.ID | left.title | right.status | right.user
1 | Title 1 | 0 | 1
3 | Title 3 | 0 | 1
4 | Title 4 | NULL | NULL
5 | Title 5 | NULL | NULL
I need right informations to do some verifications on status field if exist or not.
Here is my request for the moment :
SELECT l.title, r.status
FROM left as l
LEFT JOIN right as r ON l.id = r.left_id AND r.user = 1 AND r.status = 0
The problem is I got the line Title 2, I don't kwon how to remove this line.
If someone can help me.
Thanks

What about this
SELECT l.id, l.title, r.user, r.status
FROM left as l
LEFT JOIN right as r ON l.id = r.left_id
WHERE r.left_id IS NULL or (r.user = 1 AND r.status = 0)
The r.left_id IS NULL will keep the left records that are not in right table and or (r.user = 1 AND r.status = 0) will keep left records with requested values.

Since you use LEFT JOIN, you can't use the join condition to filter on status. Filter in the WHERE clause instead.
SELECT l.title, r.status
FROM left as l
LEFT JOIN right as r ON l.id = r.left_id
WHERE r.ID IS NULL OR (r.status = 0 and r.user = 1)

Try like this. First join with ids and then filter for your condition in where close
SELECT l.title, r.status
FROM left as l
LEFT JOIN right as r ON l.id = r.left_id
Where r.user = 1 AND r.status = 0

Related

Is it possible to filter in a left join?

I have three tables, Clients, Bills and BillsStates. I would like to get always the client and if it has bills, only the bills that can be modified. I am trying something like that:
select * from Clients
left join Bills on Bills.IDClient = Clients.IDClient
left join BillsStates on BillsStates.IDBillState = Bills.IDState
and BillsStates.AllowModify = 1
The problem with that is that I get all the bills of the client, no matter if they can be modified or not.
I have tried to with a right join, but in this case I have not get any result.
Is it possible with joins or perhaps I need some subquery? I would prefer a solution with joins, but if there is no way to do it in this way, I would accept another solution.
select * from Clients
left join Bills
inner join BillsStates on BillsStates.IDBillState = Bills.IDState
on Bills.IDClient = Clients.IDClient
and BillsStates.AllowModify = 1
The problem you have is that you only cause the BillsStates record to be excluded, because your filter is only in its join condition. Instead, you can re-order and move it into Bills's join condition.
Your query simply replaces BillsStates.* with null values where BillsStates.AllowModify = 1 condition fails:
| IDClient | Name | IDClient | IDState | Name | IDBillState | AllowModify |
|----------|------|----------|---------|--------|-------------|-------------|
| 1 | John | 1 | 1 | Bill 1 | NULL | NULL |
| 1 | John | 1 | 2 | Bill 2 | 2 | 1 |
| 2 | Jane | NULL | NULL | NULL | NULL | NULL |
Rearrange the join type and condition to get the desired result:
SELECT *
FROM Clients
LEFT JOIN (Bills
INNER JOIN BillsStates ON BillsStates.IDBillState = Bills.IDState) ON Bills.IDClient = Clients.IDClient AND BillsStates.AllowModify = 1;
| IDClient | Name | IDClient | IDState | Name | IDBillState | AllowModify |
|----------|------|----------|---------|--------|-------------|-------------|
| 1 | John | 1 | 2 | Bill 2 | 2 | 1 |
| 2 | Jane | NULL | NULL | NULL | NULL | NULL |
you can try this.
select * from Clients
left join
( select * from Bills
inner join BillsStates on BillsStates.IDBillState = Bills.IDState
and BillsStates.AllowModify = 1
) B ON B.IDClient = Clients.IDClient
use inner join between bills and BillsStates instead left join and do left join with client
select c.* from Clients c
left join Bills b on b.IDClient = c.IDClient
inner join BillsStates bs
on bs.IDBillState = b.IDState and b.AllowModify = 1
Rather than joining on a subquery, you can also re-arrange the order of joins
SELECT c.*, b.*, bs.* -- Todo: only the relevant columns
FROM Bills b
JOIN BillsStates bs ON BillsStates.IDBillState = Bills.IDState
RIGHT JOIN Clients c ON b.IDClient = c.IDClient

Get right table data on LEFT JOIN

I have a problem with my sql join request.
I need to get lines of left table who are not referenced in right table for ME (User 1) or referenced in right table with status equal to 0 and user equal to 1.
I also need the field status of right table.
Here is my two tables :
Table left
ID | title
1 | Title 1
2 | Title 2
3 | Title 3
4 | Title 4
5 | Title 5
6 | Title 6
Table right
ID | status | user | left_id
1 | 0 | 1 | 1
2 | 0 | 50 | 1
3 | 1 | 1 | 2
4 | 0 | 50 | 2
5 | 0 | 1 | 3
6 | 1 | 50 | 3
7 | 0 | 50 | 4
8 | 1 | 50 | 5
My goal is to get this result :
left.ID | left.title | right.status | right.user
1 | Title 1 | 0 | 1
3 | Title 3 | 0 | 1
4 | Title 4 | NULL | NULL
5 | Title 5 | NULL | NULL
6 | Title 6 | NULL | NULL
Here is my request for the moment :
SELECT l.id, l.title, r.user, r.status
FROM left as l
LEFT JOIN right as r ON l.id = r.left_id
WHERE r.left_id IS NULL or (r.user = 1 AND r.status = 0)
With this request I get lines ID (left table) 1 / 3 / 6. But I also need the ID 4 / 5.
Those lines isn't displayed because another user (50) as a reference, but it's not me (1).
If someone can help me to add line 4 / 5 to my result I would be happy.
Thanks
Small improvement of the query should be sufficient:
SELECT l.id, l.title, r.user, r.status
FROM left as l
LEFT JOIN right as r ON l.id = r.left_id and r.user = 1
WHERE r.left_id IS NULL or r.status = 0
(Select t1.id, t1.title,t2.status,t2.user
from tableLeft t1
right outer join tableRight t2 on t2.left_id=t1.id
where t1.id not in
(select tt2.left_id from tableRight tt2)
)
union
(select t1.id,t1.title,t2.status,t2.user
from tableLeft t1
left join tableRight t2 on t1.id=t2.left_id
where t2.status=0 and t2.user=1
)

SQL Left Join with verification on each table

First, my SQL skils are really bad.
Here is my tables :
Message :
id | title | arrayusers
1 |Title1 | a:2:{i:1;i:1;i:5;i:5;}
2 |Title2 | a:2:{i:1;i:1;i:5;i:5;}
3 |Title3 | a:2:{i:1;i:1;i:5;i:5;}
4 |Title4 | a:2:{i:1;i:1;i:5;i:5;}
5 |Title5 | a:2:{i:1;i:1;i:5;i:5;}
6 |Title6 | a:2:{i:1;i:1;i:5;i:5;}
Read :
id | status | userid | message_id
1 | 0 | 5 | 1
2 | 0 | 1 | 2
3 | 0 | 5 | 2
4 | 0 | 1 | 3
5 | 0 | 5 | 4
6 | 1 | 1 | 5
7 | 1 | 5 | 5
7 | 1 | 5 | 6
I use the userid 1 for my test :
My goal, is to get all lines from Message, where my user 1 is in arrayusers with a LIKE(i:1;i:1;)
And user 1 not in table Read (userid), with the status equal to 0. If another user (5) but not me (user 1) is in Read table, I want to see my Message line
With the data above, I want to return the id 1 / 4 from Message table
I started a request with Left join, but my request hide line id 1 and 4 because there is another user in this table with this id.
SELECT * FROM message as m
LEFT JOIN read as r ON m.id = r.message_id
WHERE m.arrayusers LIKE '%i:1;i:1;%'
AND r.id IS NULL
Hope you understand my request.
Someone can help me to debug my request ?
Thanks
I'd use NOT EXISTSor NOT IN to look up the status in the read table:
select *
from message
where arrayusers like '%i:1;i:1;%'
and id not in
(
select message_id
from read
where userid = 1
and status = 0
);
As to your own query: You are merely missing the criteria on the read table:
SELECT m.*
FROM message as m
LEFT JOIN read as r ON m.id = r.message_id and r.userid = 1 and r.status = 0
WHERE m.arrayusers LIKE '%i:1;i:1;%'
AND r.id IS NULL;
What is the use of AND r.id IS NULL? Please remove this condition and check:
SELECT * FROM message as m
LEFT JOIN read as r ON m.id = r.message_id
WHERE m.arrayusers LIKE '%i:1;i:1;%'
AND r.id IS NULL

3 table join with a calculated %

I have the following tables:
POLLS
id | name | colour
-------------------------
1 | first poll | orange
2 | secon poll | blue
3 | third poll | green
QUESTIONS
id | poll_id | what_to_ask
---------------------------
1 | 1 | How nice is stackoverflow?
2 | 1 | Why do you think that?
3 | 2 | What do you like to eat?
CHOICES
id | question_id | choice_text
------------------------------------
1 | 1 | Very nice
2 | 1 | Moderatley nice
3 | 1 | Evil
4 | 2 | Etc.
Answers
id | choice_id | poll_id | question_id
--------------------------------------
1 | 1 | 1 | 1
2 | 1 | 1 | 1
3 | 2 | 1 | 1
4 | 3 | 1 | 1
5 | 1 | 1 | 1
I'm trying to pull back the following:
A row for each choice
poll information appended to the row (this will duplicate, and it's ok)
question information appended to the row (this will duplicate, and it's ok)
% of each answer (from all answers for a particular poll)
My query (below) works well if there is an answer per choice, but if there is only one answer (and perhaps 3 choices), it'll only bring back one row.
How can i bring back each answer for a poll, and then the number of answers and % of answers on each choice.
SELECT Count(a.choice_id) AS answer_count,
q.what_to_ask,
c.id,
c.choice_text,
COALESCE (Count(a.choice_id) * 100.0 / NULLIF((SELECT Count(*)
FROM answers
WHERE poll_id = 2), 0), 0
) AS percentage
FROM choices c
RIGHT OUTER JOIN answers a
ON a.choice_id = c.id
INNER JOIN polls p ON p.id = a.poll_id
INNER JOIN questions q ON c.question_id = q.id
WHERE p.id = 2
GROUP BY c.id, q.what_to_ask, c.choice_text
I would change the join between choices and answers to a LEFT JOIN:
SELECT Count(a.choice_id) AS answer_count,
q.what_to_ask,
c.id,
c.choice_text,
COALESCE (Count(a.choice_id) * 100.0 / NULLIF((SELECT Count(*)
FROM answers
WHERE poll_id = 2), 0), 0
) AS percentage
FROM choices c
LEFT JOIN answers a
ON a.choice_id = c.id
INNER JOIN polls p ON p.id = a.poll_id
INNER JOIN questions q ON c.question_id = q.id
WHERE p.id = 2
GROUP BY c.id, q.what_to_ask, c.choice_text
In making it a RIGHT JOIN, you are telling the query to return rows only when there is a match in the answers table.

Difficult query in either Linq To Sql or SQL

I've been working on this for 2 days and I can't figure it out. I'm hoping someone smarter than me will give this a go.
Let's say I have the following tables:
Rating:
Id | Name
1 | A
2 | B
3 | C
4 | D
5 | E
Inspection:
Id | Date (mm/dd/yyyy)
1 | 01/04/2012
2 | 04/04/2012
3 | 28/03/2012
4 | 04/04/2012
Observation:
Id | InspectionId | RatingId
1 | 2 | 3
2 | 1 | 2
3 | 4 | 3
4 | 2 | 1
5 | 3 | 3
6 | 1 | 2
I want the query to return:
RatingName | Date(mm/dd/yyyy) | ObservationCount
A | 01/04/2012 | 0
B | 01/04/2012 | 1
C | 01/04/2012 | 1
D | 01/04/2012 | 0
E | 01/04/2012 | 0
A | 04/04/2012 | 1
B | 04/04/2012 | 0
C | 04/04/2012 | 2
D | 04/04/2012 | 0
E | 04/04/2012 | 0
A | 28/03/2012 | 0
B | 28/03/2012 | 0
C | 28/03/2012 | 1
D | 28/03/2012 | 0
E | 28/03/2012 | 0
So I need the number of Observations for each rating for each date. And yes I need to have the records which return 0 Observations because I'm using this data in a stacked chart and without them it throws an error. I've managed to get the above table but without the records that return 0 Observations with the following Linq To Sql query, but from this point I get stuck.
MyDataContext DB = new MyDataContext();
var data =
(from r in DB.Ratings
join o in DB.Observations on r.Id equals o.RatingId into ro
from observation in ro.DefaultIfEmpty()
join i in DB.Inspections on observation.InspectionId equals i.Id into roi
from q in roi.DefaultIfEmpty()
group q by new { Name = r.Name, Date = q.Date } into grouped
select
new
{
RatingName = grouped.Key.Name,
Date = grouped.Key.Date,
ObservationCount = grouped.Count(x => x != null)
}).OrderBy(x => x.Date);
I would appreciate an answer in either Linq To Sql or just plain old SQL, thanks!
You should try a CROSS JOIN on the two reference tables, and then OUTER JOIN back to your 'data' table - and then check whether the Observation is null... then group and sum!
SELECT
[Name],
[Date],
SUM([Exists])
FROM
(
SELECT
name,
[Date],
CASE WHEN o.Id IS NULL THEN 0
ELSE 1
END as [Exists]
FROM
Rating r CROSS JOIN
Inspection i
LEFT OUTER JOIN Observation o
ON o.RatingId = r.Id AND o.InspectionId = i.Id
) as [source]
GROUP BY [Name], [Date]
ORDER BY
[Date],
name
Translating to LINQ would be a similar two-step process - get the inner result set (checking whether observation is NULL), before grouping and summing.