I have 2 tables in this format:
TABLE1: id | name | info
and
TABLE2: name | moreinfo
I have a query which returns a list of name's from the first table. I then want to use those to get the respective moreinfo from the other table. For example, if the names a, b were returned and and in TABLE 2 I had a | apple, b | bread I would want apple and bread returned.
Any thoughts on the best way to go about doing this? Any help appreciated, thank you!
It seems that you just want a join here:
SELECT t1.name, t2.moreinfo
FROM TABLE1 t1
INNER JOIN TABLE2 t2
ON t1.name = t2.name;
If you wanted to report all names in the first table, even if they had no matching names in the second table, you could use a left join instead:
SELECT t1.name, COALESCE(t2.moreinfo, 'NA') AS moreinfo
FROM TABLE1 t1
LEFT JOIN TABLE2 t2
ON t1.name = t2.name;
Literally you seem to ask for the Apple and Bread to be returned but not neccessarily other information associated with the name. It is unclear how the query results you have are formatted.
select moreinfo from table2 where name in (YOURQUERYRESULT);
YOURQUERYRESULT can be a query returning only the name or a list with the names.
The question is answered elsewhere with join examples where you might want to substitute table with the query you have.
select a.id, a.name, a.info, b.moreinfo
from (YOURQUERY) a
join table2 b on a.name=b.name
I suggest you add an id column in table2 to identify the user and join on that field. Names change and multiple users have the same name.
Best regards,
Bjarni
Related
Query
SELECT ID, Name, Phone
FROM Table1
LEFT JOIN Table2 ON Table1.ID = Table2.ID
WHERE Table2.ID IS NULL
Problem
Finding it hard to understand why someone would left join on an ID
and then set it to NULL in the where clause?
Am I missing something here? Is there any significance to this?
Could we just omit the Table2 altogether? As in not join at all?
Any help would be much appreciated.
The query you have in the question is basically equivalent to the following query:
SELECT ID, Name, Phone
FROM Table1
WHERE NOT EXISTS
(
SELECT 1
FROM Table2
WHERE Table1.ID = Table2.ID
)
Meaning it selects all the records in Table1 that does not have a correlated record in Table2.
The execution plan for both queries will most likely be the same (Personally, I've never seen a case when they produce a different execution plan, but I don't rule that out), so both queries should be equally efficient, and it's up to you to decide whether the left join or the exists syntax is more readable to you.
I think you should have an alias for you table and specify which table each column is coming from.
Assuming Name is from table one and Phone is form table two and ID is common in both, then the Left join mentioned above may help get all users that do not have phone numbers.
Table 1
Id Name
1 John Smith
2 Jane Doe
Table 2
Id Phone
2 071 555 0863
Left Join without the where clause
ID Name Phone
1 John Smith NULL
2 Jane Doe 071 555 0863
Left Join with the where clause
ID Name Phone
1 John Smith NULL
This is one of the ways to implement the relational database operation of antijoin, called anti semi join within sql server's terminology. This is essentially "bring rows from one table that are not in another table".
The ways I cant think of doing this are:
select cols from t1 left join t2 on t1.key=t2.key where t2.key is null
select cols from t1 where key not in (select key from t2)
select cols from t1 where not exists (select 1 from t2 where t1.key=t2.key)
and even
select * from t1 where key in (select key from t1 except select key from t2)
There are some differences between these methods (most notably, the danger of null handling in the case of not in), but they generally do the same.
To address your points:
Finding it hard to understand why someone would left join on an ID and
then set it to NULL in the where clause?
As mentioned, in order to exclude results from t1 that are present in t2
Could we just omit the Table2 altogether? As in not join at all?
If you don't use the join (or any of its equivelant alternatives), you will get more results, as the rows in table1 that have the same id with any rows in table2 will be returned, too.
If joining condition column is having null value specifically ID then it is bad database design per my understanding.
As per your query below. Here are the possible scnario why where clause make sense
I am assuming that your name and phone number are coming from table2 and then you are trying to find the name and phone number whose ID is null.
If name and phone number is coming from table1 and table 2 is just having ID join and not selecting anything from table 2 then where clause is total waste.
SELECT
ID,
Name,
Phone
FROM
Table1
LEFT JOIN
Table2
ON
Table1.ID = Table2.ID
WHERE
Table2.ID IS NULL
Essentially in the above common business scenario, developers put where clause filter criteria in left join when any value is coming from right side is having non relevance data and not required to be the part of dataset then filter it out.
table1 (id, name)
table2 (id, name)
Query:
SELECT name
FROM table2
-- that are not in table1 already
SELECT t1.name
FROM table1 t1
LEFT JOIN table2 t2 ON t2.name = t1.name
WHERE t2.name IS NULL
Q: What is happening here?
A: Conceptually, we select all rows from table1 and for each row we attempt to find a row in table2 with the same value for the name column. If there is no such row, we just leave the table2 portion of our result empty for that row. Then we constrain our selection by picking only those rows in the result where the matching row does not exist. Finally, We ignore all fields from our result except for the name column (the one we are sure that exists, from table1).
While it may not be the most performant method possible in all cases, it should work in basically every database engine ever that attempts to implement ANSI 92 SQL
You can either do
SELECT name
FROM table2
WHERE name NOT IN
(SELECT name
FROM table1)
or
SELECT name
FROM table2
WHERE NOT EXISTS
(SELECT *
FROM table1
WHERE table1.name = table2.name)
See this question for 3 techniques to accomplish this
I don't have enough rep points to vote up froadie's answer. But I have to disagree with the comments on Kris's answer. The following answer:
SELECT name
FROM table2
WHERE name NOT IN
(SELECT name
FROM table1)
Is FAR more efficient in practice. I don't know why, but I'm running it against 800k+ records and the difference is tremendous with the advantage given to the 2nd answer posted above. Just my $0.02.
SELECT <column_list>
FROM TABLEA a
LEFTJOIN TABLEB b
ON a.Key = b.Key
WHERE b.Key IS NULL;
https://www.cloudways.com/blog/how-to-join-two-tables-mysql/
This is pure set theory which you can achieve with the minus operation.
select id, name from table1
minus
select id, name from table2
Here's what worked best for me.
SELECT *
FROM #T1
EXCEPT
SELECT a.*
FROM #T1 a
JOIN #T2 b ON a.ID = b.ID
This was more than twice as fast as any other method I tried.
Watch out for pitfalls. If the field Name in Table1 contain Nulls you are in for surprises.
Better is:
SELECT name
FROM table2
WHERE name NOT IN
(SELECT ISNULL(name ,'')
FROM table1)
You can use EXCEPT in mssql or MINUS in oracle, they are identical according to :
http://blog.sqlauthority.com/2008/08/07/sql-server-except-clause-in-sql-server-is-similar-to-minus-clause-in-oracle/
That work sharp for me
SELECT *
FROM [dbo].[table1] t1
LEFT JOIN [dbo].[table2] t2 ON t1.[t1_ID] = t2.[t2_ID]
WHERE t2.[t2_ID] IS NULL
You can use following query structure :
SELECT t1.name FROM table1 t1 JOIN table2 t2 ON t2.fk_id != t1.id;
table1 :
id
name
1
Amit
2
Sagar
table2 :
id
fk_id
email
1
1
amit#ma.com
Output:
name
Sagar
All the above queries are incredibly slow on big tables. A change of strategy is needed. Here there is the code I used for a DB of mine, you can transliterate changing the fields and table names.
This is the strategy: you create two implicit temporary tables and make a union of them.
The first temporary table comes from a selection of all the rows of the first original table the fields of which you wanna control that are NOT present in the second original table.
The second implicit temporary table contains all the rows of the two original tables that have a match on identical values of the column/field you wanna control.
The result of the union is a table that has more than one row with the same control field value in case there is a match for that value on the two original tables (one coming from the first select, the second coming from the second select) and just one row with the control column value in case of the value of the first original table not matching any value of the second original table.
You group and count. When the count is 1 there is not match and, finally, you select just the rows with the count equal to 1.
Seems not elegant, but it is orders of magnitude faster than all the above solutions.
IMPORTANT NOTE: enable the INDEX on the columns to be checked.
SELECT name, source, id
FROM
(
SELECT name, "active_ingredients" as source, active_ingredients.id as id
FROM active_ingredients
UNION ALL
SELECT active_ingredients.name as name, "UNII_database" as source, temp_active_ingredients_aliases.id as id
FROM active_ingredients
INNER JOIN temp_active_ingredients_aliases ON temp_active_ingredients_aliases.alias_name = active_ingredients.name
) tbl
GROUP BY name
HAVING count(*) = 1
ORDER BY name
See query:
SELECT * FROM Table1 WHERE
id NOT IN (SELECT
e.id
FROM
Table1 e
INNER JOIN
Table2 s ON e.id = s.id);
Conceptually would be: Fetching the matching records in subquery and then in main query fetching the records which are not in subquery.
First define alias of table like t1 and t2.
After that get record of second table.
After that match that record using where condition:
SELECT name FROM table2 as t2
WHERE NOT EXISTS (SELECT * FROM table1 as t1 WHERE t1.name = t2.name)
I'm going to repost (since I'm not cool enough yet to comment) in the correct answer....in case anyone else thought it needed better explaining.
SELECT temp_table_1.name
FROM original_table_1 temp_table_1
LEFT JOIN original_table_2 temp_table_2 ON temp_table_2.name = temp_table_1.name
WHERE temp_table_2.name IS NULL
And I've seen syntax in FROM needing commas between table names in mySQL but in sqlLite it seemed to prefer the space.
The bottom line is when you use bad variable names it leaves questions. My variables should make more sense. And someone should explain why we need a comma or no comma.
I tried all solutions above but they did not work in my case. The following query worked for me.
SELECT NAME
FROM table_1
WHERE NAME NOT IN
(SELECT a.NAME
FROM table_1 AS a
LEFT JOIN table_2 AS b
ON a.NAME = b.NAME
WHERE any further condition);
I have two tables. The first table contains the code number, the second table contains the name corresponding to that code number. The first table has a source code number and a target code number. The second table contains both these code numbers in separate rows. So the tables look like this
Table1
|Source_code|Target_code|
|253 |568 |
Table2
|Code|Name |
|253 |John |
|568 |Steve|
I want a result like this:
|source_code|name |target_code|name |
|253 |John |568 |Steve|
I did this by joining both the tables twice in a single query, as follows:
select A.source_code, B.name, A.target_code, C.name
from table1 A
join table2 B on A.source_code = B.code
join table2 C on A.target_code = C.code
This doesn't look like the best way to handle this query. Is there a better way to do this?
Joining to table 2 twice is the way to handle this. Why? because you have in table1 TWO foreign keys to Table2. Since tables should be joined on keys and you have two keys to one table, your solution is almost appropriate.
The only issue I see is you should be joining on table2's code not the names.
SELECT A.source_code, B.name, A.target_code, C.name
FROM table1 A
INNER JOIN table2 B
on A.source_code = B.Code
INNER JOIN table2 C
on A.target_code = C.code
I would like to return table entries from an inner join which do not have any matching entries in the second column.
Lets consider the following two tables:
Table one:
Name Number
A 1
A 2
A 4
Table two:
Name ID
A 3
The query should return Name=A ID=3. If ID would be 4, the query should not return anything. Is this even possible in SQL? Thanks for any hints!
Edit:
the joined table would look like this:
Name Number ID
A 1 3
A 2 3
A 4 3
So if I do this query I get no entries in the result set:
SELECT * FROM TABLE_ONE INNER JOIN TABLE_TWO ON TABLE_ONE.NAME=TABLE_TWO.NAME WHERE NUMBER=ID
Exactly in this situation I would like to get the Name returned!
Yes, instead of using an INNER join, use a LEFT or a FULL OUTER join. This will allow null values from the other table to appear when you have a value in one of your tables.
The FULL OUTER JOIN keyword returns all rows from the left table (table1) and from the right table (table2).
The LEFT JOIN keyword returns all rows from the left table (table1), with the matching rows in the right table (table2). The result is NULL in the right side when there is no match. (There is also a RIGHT join, but it does the same thing as the left join, just returning all rows from the RIGHT table instead of the left).
SELECT *
FROM Table2
WHERE NOT EXISTS (
SELECT *
FROM Table1
WHERE Table1.Name = Table2.Name AND Table1.Number = Table2.ID
)
As #rhealitycheck has said, a full outer join would work. I found this blog post helpful in explaining joins. P.S. I can't leave comments (Otherwise I would have).
I am trying to Join Multiple Tables.
Table 1 has distinct ID's
Table 2 has multiple names for each ID SAY 3 NAMES
Table 3 has 2 dates for each ID
when i join the Three tables, I get 6 rows of data for each ID with each of the Names appearing Twice and each of the dates appearing thrice.
I want only 3 rows to be written returning distinct 3 names and Distinct 2 dates for each ID. Is there any way to do this while using Joins in SQL?
To get one result row per row in table 1, you have to use a grouping function on the other tables. For example, if you want the last (in ascending sort order) name and date, you'd use
select t1.id,
max(t2.name),
max(t3.datefield)
from table1 t1
join table2 t2 on t2.id = t1.id
join table3 t3 on t3.id = t1.id
group by t1.id
;
What about something like this which doesn't use explicit JOINs:
SELECT T1.ID
FROM Table1 AS T1
WHERE EXISTS (
SELECT *
FROM Table2 AS T2
WHERE T2.ID = T1.ID
)
AND EXISTS (
SELECT *
FROM Table3 AS T3
WHERE T3.ID = T1.ID
);
If you preface your query with SELECT DISTINCT, you should return only the distinct rows.
If I'm understanding the question correctly, you'll want to use the GROUP BY clause
http://www.w3schools.com/sql/sql_groupby.asp
You really need to understand what it is you are trying to return and why.
Please post your example source rows and the results you want to achieve (and why).
The reason you get 6 rows is because there are 6 possibilities which match your model as you have stated it.
If this is "too many", you need to add an additional criteria, as yet unstated, that determines which date is the one you wish to display with each name.
I think you would need to do two queries.
SELECT id, name FROM names WHERE id = 1
SELECT id, date FROM dates WHERE id = 1
The type of operation that it sounds like you are trying to do isn't really valid since the results wouldn't really be a relation. (you'd have one column with 3 rows and one column with 2 rows)
Here is what I am trying to do
Table1
ID
401
Table 2
ID NameType Name
401 Primary Anu1
401 AKA Anu2
401 Maiden Anu3
Table 3
ID DateType Date
401 DOB 1983-09-25
401 DOD 2008-05-07
I write my join like
Select a.ID,b.NameType,b.Name,c.datetype,c.date
FROM Table1 a
LEFT JOIN Table2 b ON a.ID=b.ID
LEFT JOIN Table3 c ON a.ID=c.ID
This returns me
ID NameType Name Datetype Date
401 Primary Anu1 DOB 1983-09-25
401 Primary Anu1 DOD 2008-05-07
401 AKA Anu2 DOB 1983-09-25
401 AKA Anu2 DOD 2008-05-07
401 Maiden Anu3 DOB 1983-09-25
401 Maiden Anu3 DOD 2008-05-07
Is it possible to get the results like this:
ID NameType Name Datetype Date
401 Primary Anu1 DOB 1983-09-25
401 AKA Anu2 DOD 2008-05-07
401 Maiden Anu3
Or else in any other way so that the same Names or Dates are not repeated multiple times.
You have to decide what one record you want from each table and join so that only that record joins to it. The way you are displaying data you cannot do all on one line because the information is differnt in the joining tables, so you must determine what the business rule is for choosing which of the multiple records you want.
NOw if you want the primary name and both the DOb and the DOD, this is what I would do:
Select a.ID,b.NameType,b.Name,c.date as DOB, d.date as DOD
FROM Table1 a
LEFT JOIN Table2 b ON a.ID=b.ID and b.name_type = 'Primary'
LEFT JOIN Table3 c ON a.ID=c.ID and c.datetype= 'DOB'
LEFT JOIN Table3 d ON a.ID=d.ID and d.datetype= 'DOD'
Now you could do the same with multiple joins for the other names, but you probably don't know how many joins you would want in advance. Theres a solution for that too but it is more complicated and I don't have time to get it for you, but basically you have to create a process to concatenate all the namess together in a temp table of some sort (which also has the id) and then join to that temp table.