Select results from 2 tables if particular match in the third one - mysql

I have three tables
t1
--------------
userID
userEmail
userName
userType
t2
--------------
businessID
businessUserID
t3
--------------
recordID
recordBusinessID
action (ENUM: pending, open, closed)
I need to retrieve records if results found in t3 ONLY has records with with action = 'pending'.
SELECT
t2.businessID,
t1.userEmail,
t1.userName
FROM t2
LEFT JOIN t1 ON (t1.userID = t2.businessUserID)
LEFT JOIN t3 ON (t3.recordBusinessID = t2.businessID)
WHERE userType = 'active'
AND t3.action = 'pending'
t3.action != 'open'
t3.action != 'closed'
It seems like I should be getting results, because my current t3 is empty, but I don't. What am I missing?
t3 can have results but I only need to match if t3.action is nothing but 'pending'.

Comparing Null with any value returns Null even if you check not equal to. So to test that there is no corresponding value or it is not equal to something, do so:
where ... and (t3.action is null or t3.action != 'pending')

Why are you using LEFT JOIN? If you only want records that match t3.action = 'pending', a normal join will work. I would rewrite your query as:
SELECT
t2.businessID,
t1.userEmail,
t1.userName
FROM t1, t2, t3
WHERE t1.userID = t2.businessUserID
AND t2.businessID = t3.recordBusinessID
AND t1.userType = 'active'
AND t3.action = 'pending'
For this to work, you will need data in all three tables. If t3 is empty, you will get no results.
A LEFT JOIN is typically used to find ALL the rows in the left tables that match the WHERE clause, plus any data in the right tables that matches the ON clause. From the mysql manual:
If there is no matching row for the right table in the ON or USING
part in a LEFT JOIN, a row with all columns set to NULL is used for
the right table. You can use this fact to find rows in a table that
have no counterpart in another table.
If this doesn't work for you, please clarify your question and post some example data from your t1, t2, and t3 tables and I will try to help.

Related

How to update a table from another one depending on certain conditions and getting data from a third one if the conditions are not met?

I need to update a table with values coming from another table while statisfying a few conditions in the meantime.
To be more specific, the 'source' field of table1 needs to get updated by the field 'value' from table2.
These 2 tables share a common 'id' field that can be used for a join. If the 'id' of table1 doesn't have any correspondence in table2, then it should take the value of the 'source2' field from table3. If the 'source2' value is 'NULL', then it should be given a default value that we will name 'Default' in this example.
As an example, we have the table1 below, which is the one we want to update:
Then we have the table2 below, which is the source table that will be used to update table1:
Finally we have table3, which will be used if the information in table2 is unavailable:
Based on this example, I would like to write a query that would update table1 with the values below:
I have written the following query using MariaDB but obviously it is not correct:
UPDATE table1 T1
LEFT JOIN table2 T2 ON T1.id = T2.id
LEFT JOIN table3 a ON T1.id = T3.id
SET T1.source = if(T2.value is NULL, if(T3.source is NULL, 'Default', T3.source), T2.value)
WHERE T1.id = T2.id
Which parts should be amended to make it work?
You seem quite close. The left join logic is fine, we just need to adjust the conditional set and the where clause:
update table1 t1
left join table2 t2 on t2.id = t1.id
left join table3 t3 on t3.id = t1.id
set t1.source = coalesce(t2.value, t3.source, 'Default')
where t2.id is not null or t3.id is not null
coalesce() returns its first non-null argument.
The where clause ensures that we don't update rows that match in neither tables. You can remove it if you want to update all rows (those that do not match will get 'Default' assigned).

Getting unread message count from table messages and messages-seen

I have two tables, student-messages and student-messages-seen
My problem is I cannot get a query to run whereby I can say that globally '(user) has 4 unread messages'
I've created a mysqlfiddle here
I think i've gotten the second query right, But I would like someone to confirm that its correct. It produces the right number but I am not sure. Please could someone help me achieve getting the two different counts correct?
Your second query is correct. To make it global, just remove the thread id where condition. You will want an index on student-messages recipient.
I would phrase your first query as follows (changes are commented):
SELECT COUNT(*) FROM `student-messages` t1
LEFT JOIN `student-messages-seen` t2
ON t2.messageID = t1.id
AND t2.userID = t1.recipient -- moved from the WHERE clause
WHERE
t1.recipient = 1001
-- AND t2.userID = 1001 -- moved to the ON part of the JOIN
AND t2.id IS NULL -- fiter on unread messages
ORDER BY t1.ts DESC;
The second query is essentially the same, with just an additional filter on the thread:
SELECT COUNT(*) FROM `student-messages` t1
LEFT JOIN `student-messages-seen` t2
ON t2.messageID = t1.id
AND t2.userID = t1.recipient
WHERE
t1.recipient = 1001
AND t1.thread = 69
AND t2.id IS NULL
ORDER BY t1.ts DESC;

MySQL: COALESCE within JOIN

I've been trying to create a query using COALESCE with multiple forms of matching in order to join two tables -- but as far as I can tell, it hasn't been working.
Can somebody tell me what's wrong with this query?
SELECT *
FROM t1
LEFT JOIN t2 ON COALESCE(t1.id,t1.phone,t1.address) = COALESCE(t2.id,t2.phone,t2.address)
Something like that. The hope would be that the query would look to see if the unique IDs in t1 and t2 matched first, and if they didn't, it would move on to see if the phones matched, etc. It would be very helpful to attempt to match on multiple sets of criteria and to return the ones that matched any of the column and to only return the NULLs from t1 if the query couldn't find a match at all.
Edit:
By "not working," I meant that it seems like the ID matching works -- where it will return the data from t2 (and not NULLs) if the unique identifiers matched, but it doesn't move on to attempt to match on phone number or address line; this is obviously likely because the t1 table isn't returning any NULL values.
The definitions of the table is that t1 is a smaller subset of data that likely lives in the larger t2 table. Let's say that t1 is a table of about 100 people with only a few criteria: name, phone, address, id (though ID doesn't exist in every row) -- whereas t2 is a larger table of about 30,000 with much more criteria (name, phone, address, id, job, volunteer, email, notes, etc.) where I'm trying to find the 100 within the 30k.
I suspect that you want this:
SELECT *
FROM t1 LEFT JOIN
t2
ON t1.id = t2.id OR t1.phone = t2.phone or t1.address = t2.address;
This will match two rows if any of the keys match. However, you might want:
SELECT *
FROM t1 LEFT JOIN
t2
ON (t1.id = t2.id) OR
((t1.id is null or t2.id is null) AND t1.phone = t2.phone) or
(((t1.id is null or t2.id is null) and (t1.phone is null or t2.phone is null)) and t1.address = t2.address
)
Your query could be failing for a number of reasons. One possibility is type incompatibility. Another is that different rows have different distributions of NULL values, so you end up comparing difference columns, such as t1.id to t2.address.

MySQL Left Outer Join not returning NULL values for COUNT(*)

I am attempting to left outer join 2 tables: TABLE1 contains a list of users and TABLE2 contains a list of forms completed by users. I want to display the count of forms by user, created after a given date, where status equals COMPLETED.
The following query is working but not displaying NULL values (which I need):
select TABLE1.USERNAME, count(TABLE2.FORMS)
from TABLE1
left outer join TABLE2 on (TABLE2.USERID = TABLE1.USERID)
and TABLE2.STATUS = COMPLETED
where TABLE2.DATE > 20140801
group by TABLE1.USERNAME;
What do I need to do to include users with NULL counts i.e. no forms in TABLE2?
Thanks
You need to move the condition in the where clause to the on clause:
select TABLE1.USERNAME, count(TABLE2.FORMS)
from TABLE1 left outer join
TABLE2
on (TABLE2.USERID = TABLE1.USERID) and TABLE2.STATUS = COMPLETED and
TABLE2.DATE > 20140801
group by TABLE1.USERNAME;
In rows that don't match, the table2 columns are set to NULL. That, in turn, causes your where clause to fail (because comparisons to NULL return NULL, which is treated as false).
When using left outer join, filters on the second table go in the on clause. Filters on the first table go in the where clause.

Subquery That Shows Records Only Where the JOIN Doesn't Work

I created a relatively simple query in MySQL to give me a JOIN on three tables based on where first names and last names matched. From there, I wanted to write another query that would then only show me the records that didn't get matched from the JOIN -- but I couldn't quite figure out how to do it. I'm assuming that it has to do with using a subquery involving something like NOT IN and my original query, but I couldn't get it to give me the results I wanted.
This is the work-around I tried to come up with that partially functioned properly:
SELECT *,
if(t2.first=t1.first AND t2.last=t1.last, "Match", "No Match") AS "t2 Match",
if(t3.first=t1.first AND t3.last=t1.last, "Match", "No Match") AS "t3 Match"
FROM t1
LEFT JOIN t2 ON t2.first=t1.first AND t2.last=t1.last
LEFT JOIN t3 ON t3.first=t1.first AND t3.last=t1.last
WHERE if(t2.first=t1.first AND t2.last=t1.last, "Match", "No Match")="No Match"
OR if(t3.first=t1.first AND t3.last=t1.last, "Match", "No Match")="No Match";
I feel like this is something that's fairly simple and straight-forward, but I'm not getting the correct results. Can anybody help?
Thanks!
No match means that the t2 (or t3) columns are populated with Nulls in the results. So you can use IS NULL checks:
SELECT t1.*
FROM t1
LEFT JOIN t2 ON t2.first = t1.first AND t2.last = t1.last
LEFT JOIN t3 ON t3.first = t1.first AND t3.last = t1.last
WHERE t2.first IS NULL
OR t3.first IS NULL ;
And you were right, you can also write the queries using NOT IN (warning: only if the joining columns are not nullable. Otherwise you may have unexpected results):
SELECT t1.*
FROM t1
WHERE (first, last) NOT IN
( SELECT first, last FROM t2 )
OR (first, last) NOT IN
( SELECT first, last FROM t3 )
;
or using NOT EXISTS:
SELECT t1.*
FROM t1
WHERE NOT EXISTS
( SELECT 1
FROM t2
WHERE t1.first = t2.first
AND t1.last = t2.last
)
OR NOT EXISTS
( SELECT 1
FROM t3
WHERE t1.first = t3.first
AND t1.last = t3.last
) ;