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.
Related
I have two mysql tables: table_old and table_new, both with columns:
name,
surname,
birthdate
birthplace
plus others info columns. Both have an id column that doesn't match between tables. Some records are changed over time between tables, and now I need to have a third table with the records that are changed (added or deleted) between tables.
So I need to compare tables with name and surname and birthdate and birthplace.
I think that I have to use a Left Join, but I'm not sure abut syntax. Any help?
MySQL does not actually support a formal OUTER JOIN operation, but we can simulate one using a union of two joins. Use a full outer join to include records from both tables which do not match to anything in the other table:
SELECT t1.*, 'table one' AS missing
FROM table_old t1
LEFT JOIN table_new t2
ON t1.name = t2.name AND
t1.surname = t2.surname AND
t1.birthdate = t2.birthdate AND
t1.birthplace = t2.birthplace
WHERE t2.name IS NULL
UNION ALL
SELECT t2.*, 'table two'
FROM table_old t1
RIGHT JOIN table_new t2
ON t1.name = t2.name AND
t1.surname = t2.surname AND
t1.birthdate = t2.birthdate AND
t1.birthplace = t2.birthplace
WHERE t1.name IS NULL;
My criteria for claiming that a record is out of date is that any one of the fields does not match. That is, if three fields agree, but one does not, then I do not count it as a match.
Follow the link below for a running demo. You might be able to refine my answer and make it easier to group together pairs of records which are logically the same, but that would take more work.
Demo
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.
If I have two tables that I'm joining and I write the most simple query possible like this:
SELECT *
FROM t1
LEFT JOIN t2 ON t1.id = t2.id
There are a few records who have multiple rows per ID because they have multiple employers, so t1 looks like this:
ID Name Employer
12345 Jerry Comedy Cellar
12345 Jerry NBC
12348 Elaine Pendant Publishing
12346 George Real Estate
12346 George Yankees
12346 George NBC
12347 Kramer Kramerica Industries
t2 is linked with the similar IDs but with some activities that I'd like to see -- hence the SELECT * above. Though I don't want multiple rows to return if the Employer column is "NBC" -- but everything else is good.
The only other thing that matters here is that t2 is smaller than t1, because t1 is everybody and t2 are only from people who did particular activities -- so some of the matches won't return anything from t2, but I would still like them to be returned, hence the LEFT JOIN.
If I write the query like this:
SELECT *
FROM t1
LEFT JOIN t2 ON t1.id = t2.id
WHERE Employer <> "NBC"
Then it removes Jerry and George completely -- when really all I want is for the NBC row to not be returned, but to return any other rows that are associated with them.
How can I write the query while joining t1 with t2 to return each row except for the NBC ones? The ideal output would be all of the rows from t1 regardless if they match up with all of t2 except removing all of the rows with "NBC" as the employer in the return file. Basically the ideal here is to return the JOINs where they fit, but regardless remove the entire row for anybody with "NBC" as employer without removing their other rows.
The more I write about it, it seems like I should potentially just run a query prior to my JOIN to delete all the rows in t1 who have "NBC" as their employer and then run the normal query.
Basic subset filtering
You can filter either of the two merged (joined) subsets by extending the ON clause.
SELECT *
FROM t1
LEFT JOIN t2
ON t1.ID = t2.ID
AND t2.Employer != 'NBC'
If you get null values now, and you don't want them, you'd add:
WHERE t2.Employer IS NOT NULL
extended logic:
SELECT *
FROM t1
LEFT JOIN t2
ON (t1.ID = t2.ID AND t2.Employer != 'NBC')
OR (t2.ID = t2.ID AND t2.Employer IS NULL)
Using UNION
Basically, JOIN is for horizontal linking and UNION does vertical linking of datasets.
It merges to resultsets: the first without NBC, and the second (which is basically an OUTER JOIN), adds everyone in t1 which is not part of t2.
SELECT *
FROM t1
LEFT JOIN t2
ON t1.ID = t2.ID
AND t2.Employer != 'NBC'
UNION
SELECT *
FROM t1
LEFT JOIN t2
ON t1.ID = t2.ID
AND t2.Employer IS NULL
String manipulation in the resultset
If you just want to remove NBC as a string, here is a workaround:
SELECT
t1.*,
IF (t2.Employer = 'NBC', NULL, t2.Employer) AS Employer
FROM t1
LEFT JOIN t2
ON t1.id = t2.id
This basically replaces "NBC" by NULL
I've never done an inner join SQL statement before, so I don't even know if this is the right thing to use, but here's my situation.
Table 1 Columns: id, course_id, unit, lesson
Table 2 Columns: id, course_id
Ultimately, I want to count the number of id's in each unit in Table 1 that are also in Table 2.
So, even though it doesn't work, maybe something like....
$sql = "SELECT table1.unit, COUNT( id ) as count, table2.id, FROM table1, table2, WHERE course_id=$im_course_id GROUP BY unit";
I'm sure the syntax of what I'm wanting to do is a complete fail. Any ideas on fixing it?
SELECT unit, COUNT( t1.id ) as count
FROM table1 as t1 inner JOIN table2 as t2
ON t1.id = t2.id
GROUP BY unit
hope this helps.
If I understand what you want (maybe you could post an example input and output?):
SELECT unit, COUNT( id ) as count
FROM table1 as t1 JOIN table2 as t2
ON t1.id = t2.id
GROUP BY unit
Okay, so there are a few things going on here. First off, commas as joins are deprecated so they may not even be supported (depending on what you are using). You should probably switch to explicitly writing inner join
Now, whenever you have any sort of join, you also need on. You need to tell sql how it should match these two tables up. The on should come right after the join, like this:
Select *
From table1 inner join table2
on table1.id = table2.id
and table1.name = table2.name
You can join on as many things as you need by using and. This means that if the primary key of one table is several columns, you can easily create a one-to-one match between tables.
Lastly, you may be having issues because of other general syntax errors in your query. A comma is used to separate different pieces of information. So in your query,
SELECT table1.unit, COUNT( id ) as count, table2.id, FROM ...
The comma at the end of the select shouldn't be there. Instead this should read
SELECT table1.unit, COUNT( id ) as count, table2.id FROM ...
This is subtle, but the sql query cannot run with the extra comma.
Another issue is with the COUNT( id ) that you have. Sql doesn't know which id to count since table1 and table2 both have ids. So, you should use either count(table1.id) or count(table2.id)
I have the following two tables:
Table1 {T1ID, Name}
Table2 {T2ID, T1ID, Date, Value}
Date is of type DATE.
and I am looking for a SQL query to fetch only the latest value (by Date) for each T1ID for which the Name matches a specific string.
SELECT`Table2`.`T1ID`,
`Table2`.`Value`,
`Table2`.`Date`,
`Table1`.`Name`,
FROM `Table1`
INNER JOIN `Table2` ON `Table2`.`T1ID` = `Table1`.`T1ID`
WHERE `Table1`.`Name` LIKE 'Smith'
but this returns the value for several dates for the same T1ID.
How do I get only the latest value by Date?
Edit:
I am using MySQL 5.5.8
If I've understodd the question correctly:
Assuming MySQL:
SELECT`Table2`.`T1ID`,
`Table2`.`Value`,
`Table2`.`Date`,
`Table1`.`Name`
FROM `Table1`
INNER JOIN `Table2` ON `Table2`.`T1ID` = `Table1`.`ID`,
(SELECT T1ID, MAX(Date) AS 'Date' FROM Table2 GROUP BY T1ID) Table3
WHERE
`Table3`.`T1ID` = `Table2`.`T1ID`
AND
`Table3`.`Date` = `Table2`.`Date`
AND
`Table1`.`Name` LIKE 'Smith'
EDIT: Updated the code to bring back the correct result set. Removed MSSQL answer as it wasn't relevant
You have two options.
select t1.t1id, max(t1.Name) Name, max(t2.date) Date,
(select Value from table2 t22
where t22.date = max(t2.date) and t22.t1id = t2.t1id) Value
from table1 t1 left join table2 t2 on t1.t1id = t2.t1id
where Name like '%Smith%'
group by t2.t1id order by 2
OR
select mx.t1id, mx.Name, mx.Date, t2.Value
from
(
select t1.t1id, max(t1.Name) Name, max(t2.date) Date
from table1 t1 left join table2 t2 on t1.t1id = t2.t1id
where Name like '%Smith%'
group by t2.t1id
) mx left join table2 t2 on (t2.t1id = mx.t1id and t2.date = mx.date)
order by 2
Both will produce the same result. The first one takes less code but you might have performance issues with a huge set of data. The second one takes a little more code, but it is also a little more optimized. Notes on the JOIN option:
If you go LEFT JOIN (as the example shows), items in Table1 with no correspondent records on Table2 will be displayed in the result, but the values in columns Date and Value will be NULL
If you go INNER JOIN, items in Table1 with no correspondent records on Table2 will not be displayed.
EDIT
I missed one of the requirements, which was the Name matching a specific string. The code is now updated. The '%' acts like a wildcard, so it will match names like 'Will Smith' and 'Wail Smithers'. If you want a exact match, remove the wildcards ('%').
Add this to your SQL:
ORDER BY 'Date' DESC LIMIT 1