检索多个表中的多个列不存在或匹配行集(mysql php)
I would like to seek the professional advice of this forum on a mysql query which have I have spend hours trying to get it right but to no avail.
So it goes like this. Query1
below which I will retrieve the name, employer id, date, shift id, shift label, shift time, store id, store name, role id and role label from 5 tables, tblshift
, tblstore
, tblrole
, tblschedule
and employee
.
The tables tblshift, tblstore, tblrole and employer are linked to the tblschedule through their respective ids in their tables. The php variables empstore and empdate are values that are posted from the form.
$query1 = 'select a.name, a.empid, b.keydate, c.shiftid, c.shiftlabel, c.shifttime, c.shifttime2, d.storeid, d.storelabel, e.roleid, e.rolelabel from employee as a, tblschedule as b, tblshift as c, tblstore as d, tblrole as e where a.empid=b.empid and b.shiftid=c.shiftid and b.storeid=d.storeid and b.roleid=e.roleid and d.storeid='.$empstore.' and b.keydate ="' . $empdate . '"';
The above query works correctly but I would also like to retrieve the opposite of this query which are the rows not present in the query. I have tried to use the 'NOT EXIST' and also 'NOT IN' statements in my query but either the query could not run or the rows are not correct. Please see below queries using 'NOT IN' and 'NOT EXISTS'statements. From query 2 and 3, you will observe that I have used empid, keydate and shiftid because these are primary keys in my tblschedule table and the other columns are based on this uniqueness.
*$query2 = 'select a1.name, a1.empid, b1.keydate, c1.shiftid, c1.shiftlabel, c1.shifttime, c1.shifttime2, d1.storeid, d1.storelabel, e1.roleid, e1.rolelabel from employee as a1, tblschedule as b1, tblshift as c1, tblstore as d1, tblrole as e1 where a1.empid=b1.empid and b1.shiftid=c1.shiftid and b1.storeid=d1.storeid and b1.roleid=e1.roleid'
. ' where (a1.empid, b1.keydate, c1.shiftid) not in (select a2.empid, b2.keydate, c2.shiftid from employee as a2, tblschedule as b2, tblshift as c2, tblstore as d2, tblrole as e2 where a2.empid=b2.empid and b2.shiftid=c2.shiftid and b2.storeid=d2.storeid and b2.roleid=e2.roleid and d2.storeid='.$empstore.' and b2.keydate ="' . $empdate . '")';
$query3 = 'select a1.name, a1.empid, b1.keydate, c1.shiftid, c1.shiftlabel, c1.shifttime, c1.shifttime2, d1.storeid, d1.storelabel, e1.roleid, e1.rolelabel from employee as a1, tblschedule as b1, tblshift as c1, tblstore as d1, tblrole as e1 where a1.empid=b1.empid and b1.shiftid=c1.shiftid and b1.storeid=d1.storeid and b1.roleid=e1.roleid'
. ' where not exists (select 1 from employee as a2, tblschedule as b2, tblshift as c2, tblstore as d2, tblrole as e2 where a2.empid=b2.empid and b2.shiftid=c2.shiftid and b2.storeid=d2.storeid and b2.roleid=e2.roleid and d2.storeid='.$empstore.' and b2.keydate ="' . $empdate . '" and a1.empid=a2.empid and b1.keydate=b2.keydate and c1.shiftid=c2.shiftid)';*
The complicated part about the queries is that I am trying to collect datas from multiple columns of other tables and I am also filtering the datas based on the unique columns empid, keydate and shiftid. I have found some sources from this website below but could not get it working.
How to retrieve non-matching results in mysql
get the opposite results from a SELECT query
mysql "Where not in" using two columns
Thanks in advance and look forward to learning from all of you here.
regards, dennis
The original query that "works" is of the form:
SELECT a.name
, a.empid
, b.keydate
, c.shiftid
, c.shiftlabel
, c.shifttime
, c.shifttime2
, d.storeid
, d.storelabel
, e.roleid
, e.rolelabel
FROM employee a
JOIN tblschedule b
ON b.empid = a.empid
JOIN tblshift c
ON c.shiftid = b.shiftid
JOIN tblstore d
ON d.storeid = b.storeid
JOIN tblrole e
ON e.roleid = b.roleid
WHERE d.storeid = :empstore
AND b.keydate = :empdate
This should be equivalent to OP query. It's just reformatted to be more decipherable; replacing the old-school comma syntax for the join operations with the JOIN
keyword, and relocating the join predicates to the ON
clause.
I'm not understanding why OP can't just negate the predicates in the WHERE
clause. That is, replace the WHERE
clause in the query above with something like:
WHERE ( d.storeid <> :empstore )
OR ( b.keydate <> :empdate OR b.keydate IS NULL )
It seems to me that would return the set of rows OP wants to return.
But maybe there's something I'm not understanding.
In the more general case, to exclude from a set of rows returned by a query, rows that match rows returned by another query...
using an anti-join pattern is the normative approach. That's an outer join operation... return all rows from one query, along matching rows from another query, and then excluding the rows where a match was found.
In this particular case, the query would be of this form:
SELECT q1.*
FROM (
query1
) q1
LEFT
JOIN (
query2
) q2
ON q2.empid = q1.empid
AND q2.keydate = q1.keydate
AND q2.shiftid = q1.shiftid
WHERE q2.empid IS NULL
This query says to return all rows from q1, along with matching rows from q2, but excluding rows where a match was found in q2. The trick is the WHERE clause that tests for a NULL value in q2.empid. The join predicate guarantees us that q2.empid will be non-NULL when a matching row is found in q2. So the only rows that will have a NULL value in q2.empid are rows from q1 that didn't have a matching row.
q2
would be the original query; the set of rows that OP does not want to return.
q1
would be the original query, omitting the WHERE
clause. So, all of the rows... the rows OP wants to return along with the rows that OP wants to exclude.
Putting that all together, the query might look something like this:
SELECT q1.*
FROM ( -- query1 - all rows including those we are going to exclude (omit WHERE clause)
SELECT a.name
, a.empid
, b.keydate
, c.shiftid
, c.shiftlabel
, c.shifttime
, c.shifttime2
, d.storeid
, d.storelabel
, e.roleid
, e.rolelabel
FROM employee a
JOIN tblschedule b
ON b.empid = a.empid
JOIN tblshift c
ON c.shiftid = b.shiftid
JOIN tblstore d
ON d.storeid = b.storeid
JOIN tblrole e
ON e.roleid = b.roleid
) q1
LEFT
JOIN (
-- query2 - the rows that are going to be excluded
SELECT a.name
, a.empid
, b.keydate
, c.shiftid
, c.shiftlabel
, c.shifttime
, c.shifttime2
, d.storeid
, d.storelabel
, e.roleid
, e.rolelabel
FROM employee a
JOIN tblschedule b
ON b.empid = a.empid
JOIN tblshift c
ON c.shiftid = b.shiftid
JOIN tblstore d
ON d.storeid = b.storeid
JOIN tblrole e
ON e.roleid = b.roleid
WHERE d.storeid = :empstore
AND b.keydate = :empdate
) q2
ON q2.empid = q1.empid
AND q2.keydate = q1.keydate
AND q2.shiftid = q1.shiftid
WHERE q2.empid IS NULL
But again, for this particular case, the anti-join pattern seems like a round-about way of simply negating the predicates in the WHERE clause.
thanks for the detailed explanations and writeup and sorry for replying late cos I was trying to digest the info and experimenting with your examples.
My final query is as below and working.
$query = 'SELECT q1.* FROM (SELECT a.name , a.empid, b.keydate, c.shiftid, c.shiftlabel, c.shittime, c.shittime2, d.storeid, d.storelabel, e.roleid, e.rolelabel FROM tblschedule b JOIN employee a ON b.empid = a.empid JOIN tblshift c ON c.shiftid = b.shiftid JOIN tblstore d ON d.storeid = b.storeid JOIN tblrole e ON e.roleid = b.roleid) q1'
.' LEFT JOIN (SELECT a.name , a.empid, b.keydate, c.shiftid, c.shiftlabel, c.shittime, c.shittime2, d.storeid, d.storelabel, e.roleid, e.rolelabel FROM tblschedule b JOIN employee a ON b.empid = a.empid JOIN tblshift c ON c.shiftid = b.shiftid JOIN tblstore d ON d.storeid = b.storeid JOIN tblrole e ON e.roleid = b.roleid WHERE d.storeid ='.$empstore.' AND b.keydate ="'. $empstartdate.'") q2'
.' ON q2.empid = q1.empid'
.' AND q2.keydate = q1.keydate'
.' AND q2.shiftid = q1.shiftid'
.' WHERE q2.storeid IS NULL and q2.keydate is null and q2.empid is null';
by the way, this is the first question I have posted on this website and how do I go about marking your answer as the correct one.
thanks and have a nice day.