MySQL Left Join with child IsDeleted Flag - mysql

I have been searching google, but can't seem to exactly figure out my case. What I am trying to do is join a customer who has a list of jobs associated to him. I want the customer and any "active" jobs, in other words they haven't been deleted. The deleted flag is whether or not the datedeleted column is null or not.
So Lets say I have a table Customer:
ID - Name
1 - Joe Blow
2 - John Smith
And I also have a table of Jobs
ID - CustomerId - Name - DateDeleted
1 - 1 Build Fence NULL
2 - 2 - Clean Yard - 25/12/2014
What I want to do is get a customer with a list of their jobs. Now I know that if I do a join with Jow Blow it works fine, but it doesn't work when I want to get John Smith, it doesn't return any rows because I am checking the Deleted flag which then doesn't return any rows for jobs which in turn doesn't return any customer.
SELECT c.id, c.name, j.name as JobName from customer c
left join job j on c.id = j.CustomerId
where c.id = :id AND j.date_deleted IS NULL
And I want a results to be either:
Id - Name - JobName
1 - Jow Blow - Build Fence
or
Id - Name - JobName
2 - John Smith - NULL
Any help would be greatly appreciated. Thanks!

The problem is in your WHERE clause. When you add the AND j.date_deleted IS NULL, you are turning your OUTER JOIN into an INNER JOIN. You will need to move this condition to the ON clause of your OUTER JOIN.
Use this instead:
Select c.id,
c.name,
j.name As JobName
From customer c
Left Join job j On c.id = j.CustomerId
And j.date_deleted IS NULL
Where c.id = :id

Just try with this approach.As per my understanding you want to show all those jobs which has no date.
select e.Id,e.Name,( case when j.date is null then j.name else null end) as Jobprocess from
customer e
left join job j on e.Id=j.custId

Related

How to remove matches between unioned tables with joined tables

I have 2 Tables
emails and fields
emails is just a list of emails and their unique identifiers and fields is in fifth normal form (i think) with a column of values(that ID data in the second column),a data column, and ids that link to the uuids in the first table.
For example:
Table 1:
ID EMAIL
-- -----
1a2 Test#Test
2a3 email#email|
3a4 add#add
Table 2:
value Data ID
----- ------ ---
1 123 Main 1a2
2 John Smith 1a2
3 US 1a2
4 555-555-5555 1a2
In this case, table 2 only shows the data for "1a2" because they were the only one to fill them out.
So I'm trying to make a table, that couples email to the corresponding country codes AND also provides the emails that haven't registered country codes as a Null value
I've tried using UNION to display all the emails from Table 1 on Table 2 after joining table 1 and 2 on ID and then using where for the value but that shows all the emails that have the proper country code and then duplicates them in the UNIONed portion as well.
This is an example of what I'm getting:
EMAIL COUNTRY CODE
----- ------------
test#test US
test#test NULL
email#email NULL
add#add NULL
You'll notice that test#test is duplicated due to the union not having the proper filter
My code looks like:
select
e.email as "Email",
f.value as "Country Code"
from
email e
join
fields f
ON e.id = f.id
where
f.value = '3'
[[and f.data like concat({{CountryCode}},'%')]]
-- curly brackets are for user entered variables
Anyways, all said and done. I'm looking for a table that appears something like this with No Duplicates
EMAIL COUNTRY CODE
----- ------------
test#test US
email#email NULL
add#add NULL
You are storing your user metadata in key value format. One approach here is to left join the user table to a subquery which pivots to find the country:
SELECT
e.EMAIL,
f.country_code
FROM email e
LEFT JOIN
(
SELECT ID, MAX(CASE WHEN value = 3 THEN Data END) AS country_code
FROM fields
GROUP BY ID
) f
ON e.ID = f.ID;
Demo
You can try using max() aggregation and group by -
select
e.email as "Email",
max(f.value) as "Country Code"
from
email e
left join
fields f
ON e.id = f.id
where
f.value = '3'
[[and f.data like concat({{CountryCode}},'%')]]
group by e.email
You can make the field you're interested in part of your LEFT JOIN condition.
SELECT
e.EMAIL,
f.data as countryCode
FROM email e
LEFT JOIN fields f ON f.value=3 AND e.ID=f.ID
Users with a value for that field will return it. Users without will simply be NULL due to it being a LEFT join.
If you want to do a condition on the fields value, then you'll need to include a check for null as well.
SELECT
e.EMAIL,
f.data as countryCode
FROM email e
LEFT JOIN fields f ON f.value=3 AND e.ID=f.ID
WHERE
f.data IS NULL
OR f.data='US'
Well I feel pretty silly after posting this, but I found the solution and am going to post it in case someone else bumps into the issue.
left join
fields f
ON e.id = f.id
where
f.value = '3' OR f.value is null
[[and f.data like concat({{CountryCode}},'%')]]
I just needed to add an "OR" for null values. Sorry if I wasted anyone's time, I was clearly overcomplicating things and managed to achieve what I was trying to do without a union at all.

Issue faced while retrieving the record having null value with SQL Query

I am facing issue to obtain the record of the employee which consists of the null value in some particular columns.
Scenario:
I have got the following table:
employee_id Name job_id department_id
(int) (varchar) (int) (int)
-------------------------------------------------------
1 Sita 1 4
2 Ram 3 5
3 Hari (null) (null)
The query to SELECT the employee record is given below:
SELECT
employees.EMPLOYEE_ID,
employees.Name,
jobs.job_title,
departments.department_name,
FROM
employees,jobs,departments
WHERE
jobs.job_id=employees.job_id
AND departments.department_id=employees.department_id
Test Done:
With the above query, I'm able to fetch the records of the employees having employee_id = 1 and employee_id = 2 .
Problem Faced:
But with the same query, I'm not able to fetch the record of the employee having employee_id = 3.
Required Outcome:
The record of the the employee_id = 3 should be displayed.
What correction do I need to get my desired outcome?
Never use comma in the FROM clause. Always use proper, explicit, standard JOIN syntax.
You simply want a LEFT JOIN:
SELECT e.EMPLOYEE_ID, e.Name, j.job_title, d.department_name,
FROM employees e LEFT JOIN
jobs j
ON j.job_id = e.job_id LEFT JOIN
departments d
ON d.department_id = e.department_id;

Sql query join on not eqal

So i have this relational model for hospital (not made by me).
Patient (has an adress and an id), hospital (has id and address), and also there's a table for relationship representing placement in the hospital (hospital.id, patient.id) (also there's other tables, but they don't matter in this query);
The purpose of the query is to find hospitals where is no placed patients from from other cities than hospital's one (on condition that address only contains city).
The problem that i have is theoretical, i don't really know if to use full outer join with a or b null, or something else in the query that finds hospitals containing "foreign" patients, (like join hospital with its placement and then full outer join with a or b table record null, but that leads to a question will i get results in the query? Because i need cities that don't match but all the explanations of that join are about .
Thanks to all who embraced my utterly imperfect english and understood it.
Upd.
Patient:
id=1, city =A;
id=2, city =B;
id=3, city =B;
id=4, city =A;
id=5, city =C;
Hospital:
Id =1, city=A
id =2, city=B;
Placement:
h.id p.id
1 1
1 4
2 2
2 3
2 5
Expected results is "1", id of the first hospital (where's no patients from other city) and others with that "feature"
my query is like
select id from hospital where id not in
(select id,address from hospital inner join placement on h.id=placement.h.id as b inner join patient on placement.p.id=p.id where hospital.address<>patient.address )
Sorry for the delay
Is shawn's query correct?
Can i use h.id instead 1? Idk if our teacher would accept that, because he's never showed us something like that and in 10 years he hasn't managed to create an example of that database for students to test queries on.
select * from hospitals h
where not exists (
select 1 -- dummy value, use h.id if you prefer
from patients p inner join placement pl on pl.pid = p.id
where pl.hid = h.id and p.city <> h.city
)
or
select h.id
from hospitals h
left outer join
placement pl inner join patients p on p.id = pl.pid
on pl.hid = h.id
group by h.id
-- this won't count nulls resulting from zero placements for that hospital
-- as long as standard sql null comparisons are active
having count(case when h.city <> p.city then 1 end) = 0
Looks like it works to me: http://rextester.com/BTJB59061

SELECT query on multiple table

I have two tables,
ID NAME
-------------------
12 Jon Doe
4 Jane Doe
9 Sam Doe
AND
MemID Cat# DateChkOut DateDue DateRet
4 T 430.98 1956 15-Mar-2011 14-Jun-2011 31-May-2011
12 B 125.2 2013 15-Mar-2011 14-Jun-2011
4 T 430.98 1956 27-Dec-2012 25-Mar-2013
Now I need to list the `members’ names who have never checked out a book.
SELECT Name FROM MEMBER, CHECKOUT WHERE ID != MemID;
did not work. Any suggestions?
You need to use a LEFT JOIN in this case.
Try the following:
SELECT M.Name
FROM MembersTable M
LEFT JOIN CheckoutTable C On C.MemId = M.ID
WHERE C.MemId IS NULL
Edit:
LEFT JOIN works in this situation because it joins the two tables on a common key - in this case the MemId. Since this is a LEFT JOIN it will take everything that exists in the Left table (Members) and if a match is found, it will include everything on the Right table (Checkout). But if no match is found in the Checkout table, everything on that side will be NULL.
So, all you would need to do is check to see if the Right side is NULL.
Hope this makes sense :)
SELECT name FROM member WHERE id NOT IN (SELECT MemID FROM checkout)
I think it should do the job
Use a correlated subquery with NOT EXISTS.
SELECT Name
FROM MEMBER a
WHERE NOT EXISTS(SELECT 1 FROM CHECKOUT b WHERE b.MemID = a.ID)
See Optimizing Subqueries with EXISTS Strategy.
If you use NOT IN in the query, that can be slow for large queries, try instead following using JOIN, which will be way faster:
SELECT MEMBER.NAME FROM MEMBER
LEFT JOIN CHECKOUT ON CHECKOUT.MemID = MEMBER.ID
WHERE CHECKOUT.MemID IS NULL:
Because you may want more fields returned an outer join is good method of accomplishing this.
SELECT Name
FROM MEMBER M
LEFT JOIN CHECKOUT C
on M.ID = C.MemID
WHERE C.MemID is null;
Try this:
SELECT M.ID,M.Name
FROM MEMBER M LEFT JOIN
CHECKOUT C ON C.MemID=M.ID
WHERE C.DateRet IS NULL
It will select the user details whose DateRet is null.
Sample result:
ID NAME
-------------------
12 Jon Doe
4 Jane Doe
You have to use a where in and check for the DateChkOut:
SELECT Name
FROM MEMBER
WHERE ID not in
( select MemID
from CHECKOUT
where DateChkOut is not null
)
SELECT id,
name
FROM Member
WHERE id NOT IN
(SELECT memid
FROM checkout)
or
SELECT id,
name
FROM member m
LEFT OUTER JOIN checkout c ON m.id = c.memid
WHERE c.memid IS NULL

Mysql (conditional?) query from two tables

Not sure if I have phrased the title properly, but here it goes. I have these two tables:
table:staff
id Name groupId Status
1 John Smith 1 1
2 John Doe 1 1
3 Jane Smith 2 1
4 Jerry Smith 1 1
table:jobqueue
id job_id staff_id jobStatus
1 1 1 1
2 2 1 1
3 5 2 1
4 7 3 0
Now, what I need to do is to find the staff with the least amount of job assigned to him which I am able to do by querying the jobqueue table.
SELECT min(cstaff),tmp.staff_id FROM (SELECT t.staff_id, count(staff_id) cstaff from jobqueue t join staff s on t.staff_id=s.id join group g on s.groupId=g.id where g.id=26 GROUP BY t.id ) tmp
This works fine, but the problem is if a staff is not assigned to any job at all, this query wont get them, because it only queries the jobqueue table, where that particular staff won't have any entry. I need to modify the query to include the staff table and if a staff is not assigned any job in the jobqueue then I need to get the staff details from the staff table. Basically, I need to find staff for a group who are not assigned any job and if all staffs are assigned job then find staff with the least amount of jobs assigned. Could use some help with this. Also, tagging as Yii as I would like to know if this is doable with Yii active-records. But I am okay with a plain sql query that will work with Yii sql commands.
not sure that it is optimal query, but it works:
select d.groupId, d.name, (select count(*) from jobqueue as e where e.staff_id=d.id) as jobassigned
from staff as d
where d.id in (
select
(
select a.id
from staff as a
left outer join
jobqueue as b
on (a.id = b.staff_id)
where a.groupId = c.groupId
group by a.id
order by count(distinct job_id) asc
limit 1
) as notassigneduserid
from (
select distinct groupId from staff
) as c)
maybe need some comments:
c query is needed to get all distinct groupId - if you have separate table for this, you can replace it
notassigneduserid statement for each groupId select user with minimal job count
d query is needed to fetch actual user names, groupId for all found "unassigned users" and present it
here is the results for data from question:
Group Staff Jobs assigned
1 Jerry Smith 0
2 Jane Smith 1
with
counts as (
select s.groupId
, s.id
, (select count(*) from jobqueue where staff_id = s.id) count
from staff s
group by s.id, s.groupId),
groups as (
select groupId, min(count) mincount
from counts
group by groupId)
select c.groupId, c.id, c.count
from counts c
join groups g on c.groupId = g.groupId
where c.count = g.mincount
This SQL will give you all the staff with the minimum number of jobs in each group. It might be that more than one staff has the same minimum number of jobs. The approach is to use common table expressions to build first a list of counts, and then to retrieve the minimum count for each group. Finally I join the counts and groups tables and retrieve the staff that have the minimum count for each group.
I tested this on SQL Server, but the syntax should work for MySQL as well. To your data I added:
id Name groupId Status
5 Bubba Jones 2 1
6 Bubba Smith 1 1
and
id job_id staff_id jobStatus
5 4 5 1
Results are
group name count
1 Bubba Smith 0
1 Jerry Smith 0
2 Bubba Jones 1
2 Jane Smith 1
BTW, I would not try to do this with active record, it is far too complex.
As Ilya Bursov said this answer wasn't respond exactly what was asked. So here is a more optimized solution:
SELECT *
FROM (
SELECT s.id as id_staff, s.Name, s.groupId, count(distinct t.id) as jobsXstaff
FROM staff s
LEFT JOIN jobqueue t ON s.id=t.staff_id
GROUP BY s.id, s.groupId
ORDER BY s.groupId, jobsXstaff
) tmp
GROUP BY groupId
Old answer below.
This works but without table group which I don't create. You can simply join table groups as you did:
SELECT min(cstaff),tmp.id
FROM (
SELECT s.id, count( staff_id ) cstaff
FROM jobqueue t
RIGHT JOIN staff s ON t.staff_id = s.id
GROUP BY t.id
) tmp
As you see you need to get all values from table staff (right join) and select the id staff from it's own table (s.id instead of t.staff_id). Also you have to get tmp.id instead of staff_id now.