I have a MYSQL query selecting from two tables. The second table may not necessarily have a matching join id to the first table. Let's use these two as examples:
++++++++++++++++++
table: t1
column: id
column: test_id
column: info
table t2
column: t2id
column: test_id
column: extra_info
++++++++++++++++++
I inserted these following rows into the table (this is just all pseudo at the moment):
insert into t1 values (1, 4, "asd")
insert into t1 values (2, 25, "dfg")
insert into t2 values (1, 25, "123")
Now my goal is to join the two tables together, but I am having issues with the join. if the second table (t2) doesn't seem to have a matching row it makes the join column in the original table NULL? Why is it doing this? Here is an example query:
SELECT * FROM `t1` LEFT JOIN `t2` ON (`t1.test_id` = `t2.test_id`) WHERE `t1.id` = 1;
Now I have absolutely no issues if they match up, but for some reason if there is no row that exists, then test_id from t1 is being set as NULL...My goal is to join these two tables if there is something to join. EDIT: If there is nothing to join then i want to just return the data from t1 by either having all the t2 columns set to NULL or just returning the t1 data. Do I need to do a subquery? and if so what would it be? Thanks :)
Use an INNER JOIN.
SELECT * FROM `t1`
INNER JOIN `t2` ON (`t1.test_id` = `t2.test_id`)
WHERE `t1.id` = 1;
Documentation on JOIN types: http://dev.mysql.com/doc/refman/5.7/en/join.html
That's the definition of LEFT JOIN. It returns rows even if only the left half of the join matches, filling the right side with NULL if needed.
If you want to get only the rows where there's a corresponding right side, use just JOIN instead of LEFT JOIN.
When joining, it is advisable not to use "SELECT *", particularly when you have similarly named columns in multiple tables, as this introduces ambiguity, and you may not get the result intended. I would suggest instead:
SELECT t1.*, t2.t2id, t2.extra_info
FROM `t1`
LEFT JOIN `t2`
ON (`t1.test_id` = `t2.test_id`)
WHERE `t1.id` = 1;
There are multiple different types of joins. I think what you want is "inner join", where only rows that have a match are returned. Check out http://www.w3schools.com/sql/sql_join.asp
Related
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 am trying to join two tables together in a view. My first table combines two tables but now I am trying to join that table to a second table. I am trying to use a join to make sure the second table which contains roles which are performed by the user, matches up with the first table but if there are no records in the second table, I still want all the records from table 1. I know my explanation is a little confused, so my code is as follows:
CREATE VIEW `data` AS SELECT
`information`.`username`, `information`.`department`,
`information`.`company`, `information`.`title`,
`information`.`internal_number` AS `user_internal_phone`,
`information`.`external_number`AS `user_external_phone`,
`information`.`mail`, `information`.`cn` AS `name`, `information`.`hours`,
`information`.`languages`, `information`.`functions`,
`information`.`service` AS `contact_point_name_one`,
`information`.`subservice` AS `contact_point_name_two`,
`information`.`internal_phone` AS `contact_internal_phone`,
`information`.`external_phone` AS `contact_external_phone`,
`information`.`keywords`, `information`.`description`,
`information`.`provided_by`, `information`.`id`,
GROUP_CONCAT(`staff_roles`.`role` SEPARATOR ', ') AS `roles`,
`staff_roles`.`user`
FROM `information`
CROSS JOIN `staff_roles` ON `information`.`username` = `staff_roles`.`user`;
I get an error when I do an outer join and a cross join and an inner join both return rows where there is a row in both tables yet I want it to display the rows where there is nothing in the second table as well. The purpose of using a join is so that, where there is a match, the row from table 2 should match the row on table 1
Use LEFT JOIN. 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.
Just replace your CROSS JOIN with LEFT JOIN
Simply use LEFT JOIN instead of CROSS JOIN:
CREATE VIEW `data` AS SELECT
...
FROM `information`
LEFT JOIN `staff_roles` ON `information`.`username` = `staff_roles`.`user`;
Take a look at http://www.w3schools.com/sql/sql_join_left.asp for further details about why LEFT JOIN.
I'm doing some maintenance work on a database application and I've discovered that, joy of joys, even though values from one table are being used in the style of foreign keys, there's no foreign key constraints on the tables.
I'm trying to add FK constraints on these columns, but I'm finding that, because there's already a whole load of bad data in the tables from previous errors which have been naively corrected, I need to find the rows which don't match up to the other table and then delete them.
I've found some examples of this kind of query on the web, but they all seem to provide examples rather than explanations, and I don't understand why they work.
Can someone explain to me how to construct a query which returns all the rows with no matches in another table, and what it's doing, so that I can make these queries myself, rather than coming running to SO for every table in this mess that has no FK constraints?
Here's a simple query:
SELECT t1.ID
FROM Table1 t1
LEFT JOIN Table2 t2 ON t1.ID = t2.ID
WHERE t2.ID IS NULL
The key points are:
LEFT JOIN is used; this will return ALL rows from Table1, regardless of whether or not there is a matching row in Table2.
The WHERE t2.ID IS NULL clause; this will restrict the results returned to only those rows where the ID returned from Table2 is null - in other words there is NO record in Table2 for that particular ID from Table1. Table2.ID will be returned as NULL for all records from Table1 where the ID is not matched in Table2.
I would use EXISTS expression since it is more powerful, you can e.g. more precisely choose rows you would like to join. In the case of LEFT JOIN, you have to take everything that's in the joined table. Its efficiency is probably the same as in the case of LEFT JOIN with null constraint.
SELECT t1.ID
FROM Table1 t1
WHERE NOT EXISTS (SELECT t2.ID FROM Table2 t2 WHERE t1.ID = t2.ID)
SELECT id FROM table1 WHERE foreign_key_id_column NOT IN (SELECT id FROM table2)
Table 1 has a column that you want to add the foreign key constraint to, but the values in the foreign_key_id_column don't all match up with an id in table 2.
The initial select lists the ids from table1. These will be the rows we want to delete.
The NOT IN clause in the where statement limits the query to only rows where the value in the foreign_key_id_column is not in the list of table 2 ids.
The SELECT statement in parenthesis will get a list of all the ids that are in table 2.
Let we have the following 2 tables(salary and employee)
Now i want those records from employee table which are not in salary.
We can do this in 3 ways:
Using inner Join
select * from employee
where id not in(select e.id from employee e inner join salary s on e.id=s.id)
Using Left outer join
select * from employee e
left outer join salary s on e.id=s.id where s.id is null
Using Full Join
select * from employee e
full outer join salary s on e.id=s.id where e.id not in(select id from salary)
Where T2 is the table to which you're adding the constraint:
SELECT *
FROM T2
WHERE constrained_field NOT
IN (
SELECT DISTINCT t.constrained_field
FROM T2
INNER JOIN T1 t
USING ( constrained_field )
)
And delete the results.
From similar question here MySQL Inner Join Query To Get Records Not Present in Other Table I got this to work
SELECT * FROM bigtable
LEFT JOIN smalltable ON bigtable.id = smalltable.id
WHERE smalltable.id IS NULL
smalltable is where you have missing records, bigtable is where you have all the records. The query list all the records that not exist in smalltable but exists on the bigtable. You could replace id by any other matching criteria.
I Dont Knew Which one Is Optimized (compared to #AdaTheDev
) but This one seems to be quicker when I use (atleast for me)
SELECT id FROM table_1 EXCEPT SELECT DISTINCT (table1_id) table1_id FROM table_2
If You want to get any other specific attribute you can use:
SELECT COUNT(*) FROM table_1 where id in (SELECT id FROM table_1 EXCEPT SELECT DISTINCT (table1_id) table1_id FROM table_2);
You could opt for Views as shown below:
CREATE VIEW AuthorizedUserProjectView AS select t1.username as username, t1.email as useremail, p.id as projectid,
(select m.role from userproject m where m.projectid = p.id and m.userid = t1.id) as role
FROM authorizeduser as t1, project as p
and then work on the view for selecting or updating:
select * from AuthorizedUserProjectView where projectid = 49
which yields the result as shown in the picture below i.e. for non-matching column null has been filled in.
[Result of select on the view][1]
You can do something like this
SELECT IFNULL(`price`.`fPrice`,100) as fPrice,product.ProductId,ProductName
FROM `products` left join `price` ON
price.ProductId=product.ProductId AND (GeoFancingId=1 OR GeoFancingId
IS NULL) WHERE Status="Active" AND Delete="No"
SELECT * FROM First_table
MINUS
SELECT * FROM another
How to select rows with no matching entry in Both table?
select * from [dbo].[EmppDetails] e
right join [Employee].[Gender] d on e.Gid=d.Gid
where e.Gid is Null
union
select * from [dbo].[EmppDetails] e
left join [Employee].[Gender] d on e.Gid=d.Gid
where d.Gid is Null
i have two tables in a DB with the following structure:
table 1: 3 rows - category_id, product_id and position
table 2: 3 rows - category_id, product_id and position
i am trying to set table 1 position to table 2 position where category and product id is the same from the tables.
below is the sql i have tried to make this happen but returns MySQL error 1242 - subquery returns more then 1 row
UPDATE table1
SET position = (
SELECT position
FROM table2
WHERE table1.product_id = table2.product_id AND table1.category_id = table2.category_id
)
The solution is very simple and it can be done in two simple steps. The first step is just a preview of what will be changed, to avoid destroying data. It can be skipped if you are confident of your WHERE clause.
Step 1: preview the changes
Join the tables using the fields you want to match, select everything for visual validation of the match.
SELECT t1.*, t2.*
FROM table1 t1
INNER JOIN table2 t2
ON t1.category_id = t2.category_id
AND t1.product_id = t2.product_id
You can also add a WHERE clause if only some of the rows must be modified.
Step2: do the actual update
Replace the SELECT clause and the FROM keyword with UPDATE, add the SET clause where it belongs. Keep the WHERE clause:
UPDATE table1 t1
INNER JOIN table2 t2
ON t1.category_id = t2.category_id
AND t1.product_id = t2.product_id
SET t1.position = t2.position
That's all.
Technical considerations
Indexes on the columns used on the JOIN clause on both tables are a must when the tables have more than several hundred rows. If the query doesn't have WHERE conditions then MySQL will use indexes only for the biggest table. Indexes on the fields used on the WHERE condition will speed up the query. Prepend EXPLAIN to the SELECT query to check the execution plan and decide what indexes do you need.
You can add SORT BY and LIMIT to further reduce the set of changed rows using criteria that cannot be achieved using WHERE (for example, only the most recent/oldest 100 rows etc). Put them on the SELECT query first to validate the outcome then morph the SELECT into an UPDATE as described.
Of course, indexes on the columns used on the SORT BY clause are a must.
You can run this query to see what is happening:
SELECT product_id, category_id, count(*), min(position), max(position)
FROM table2
GROUP BY product_id, category_id
HAVING COUNT(*) > 1;
This will give you the list of product_id, category_id pairs that appear multiple times in table2. Then you can decide what to do. Do you want an arbitrary value of position? Is the value of position always the same? Do you need to fix the table?
It is easy enough to fix the particular problem by using limit 1 or an aggregation function. However, you may really need to fix the data in the table. A fix looks like:
UPDATE table1 t1
SET t1.position = (SELECT t2.position
FROM table2 t2
WHERE t2.product_id = t1.product_id AND t2.category_id = t1.category_id
LIMIT 1
);
I am LEFT JOINing multiple tables as such:
SELECT * FROM table1
LEFT JOIN table2 ON table1.id = table2.fid
LEFT JOIN table3 ON table1.id = table3.fid
LEFT JOIN table4 ON table1.id = table4.fid
LEFT JOIN table5 ON table1.id = table5.fid
LEFT JOIN table6 ON table1.id = table6.fid
WHERE table2.id = '5184'
Reason being is simply because I tried regular JOIN and no results are returned unless at least one record exists in tables1-6 which is often not the case. As it stands this returns the info I want, but the problem is that a lot of the field names repeat and when I do a 'mysql_fetch_array()' in PHP it hides any field with a duplicate name.
What type of query can I do to avoid this? At the very least, how do I construct the query such that it doesn't include all the double fields which are NULL?
Thanks
I can't help but notice that you aren't selecting anything from your left joined tables. It's my understanding that joins are typically used to pull data from secondary tables where something in those tables matches a field in the primary table. I pulled this example from W3 schools:
SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo
FROM Persons
LEFT JOIN Orders
ON Persons.P_Id=Orders.P_Id
ORDER BY Persons.LastName
And unless this is a typo, you are selecting everything from table 1 where an id column = 5186 in table 2.
You state that your query returns the info you want, but with many nulls and overwritten values. To fix this, I would restructure your query to follow the typical format shown above, and make sure you have a proper use of the left join statement.
EDIT AFTER OP COMMENTS:
You are naming a row id from table2, so I assume you should start by pulling fid from table 2 where table2.id = 5186, then left join from the other tables where table1.id = table2.fid.
SELECT table2.fid, table1.id, table3.id, [add other tables here, and revise field names to select the ones you want]
FROM table2
LEFT JOIN table1 ON table1.id = table2.fid
LEFT JOIN table 3 ON table3.fid = table1.id
[add left joins to other tables here]
WHERE table2.id = '5186'
If this doesn't help, I suggest being more specific towards what information the tables hold, and what you are searching for. A little more detail may help us help you.