MySQL: join with an unused table increases execution time? - mysql

Assuming t is a large table, and the following two queries
SELECT t1.value, t2.value
FROM t as t1 JOIN t as t2
ON t1.id = t2.id
WHERE t1.key = '123'
and
SELECT t1.value, t2.value
FROM t as t1 JOIN t as t2 JOIN t as t3
ON t1.id = t2.id
WHERE t1.key = '123'
the second one having a JOIN with a table that is not used in the SELECT.
The second query executes much slower. I expected that MySQL would figure out that the third JOIN is not used and will just ignore it. But it does not?

Your second query doesn't have an ON clause for the second join:
SELECT t1.value, t2.value
FROM t as t1
JOIN t as t2
JOIN t as t3 ON t1.id = t2.id
WHERE t1.key = '123';
This means that every matching record in t1 will be joined onto every record in t2. This is, perhaps, what you meant:
SELECT t1.value, t2.value
FROM t as t1
JOIN t as t2 ON t1.id = t2.id
JOIN t as t3 ON t1.id = t3.id
WHERE t1.key = '123';
This will perform much more reasonably because it isn't creating a huge number of results.
If you intended to do a full join onto t3:
SELECT t1.value, t2.value
FROM t as t1
JOIN t as t2 ON t1.id = t2.id
JOIN t as t3
WHERE t1.key = '123';
Then this will be slower because, even though you are not SELECTing a field from t3 it does change the output because it produces extra rows.
See here for examples http://sqlfiddle.com/#!9/e86c9/3

This is because the default JOIN in mySQL implies INNER JOIN.
The third join will not be ignored because this will alter the eventual data set you get back after executing the query.
This stackoverflow question contains more detailed information

It is not that the MySQL optimizer isn't smart enough to remove the unused query, it is just that you are using the wrong syntax here. As the documentation states, your query will be performed as:
JOIN t as t2 JOIN t as t3 --> t2 CROSS JOIN t3
The syntax you are using isn't standard SQL and cannot be used in any SQL standard compliant database. Take a look at the specific MySQL JOIN documentation here .

Related

mySQL group two INNER JOINs

I basically want to join the result of two INNER JOINs.
On this scheme I want to get the three arrows results combined.
I've tried INNER / LEFT combinations but it doesn't do the trick.
I think a nested request could be the solution but how ?
Thanks
The answer was actually simple : UNION
SELECT t1.*
FROM
(SELECT t1.*
FROM table1 t1 JOIN table2 t2 ON t2.id = i.client_id
UNION
SELECT t1.*
FROM t1 t1 JOIN table3 t3 ON t1.id = t3.client_id) as q1
;
I'd use logic to express the condition T1.id exists in T2 or T3 more directly, and certainly avoid use of DISTINCT or UNION.
Options could be to use EXISTS directly (As this is immure to the possibility of duplication cause by 1:many joins)...
SELECT
t1.*
FROM
table1 t1
WHERE
EXISTS (SELECT * FROM table2 t2 WHERE t2.t1_id = t1.id)
OR
EXISTS (SELECT * FROM table3 t3 WHERE t3.t1_id = t1.id)
Or to LEFT JOIN twice and then exclude unwanted rows. (This assumes that the joins are never 1:many, which would introduce duplication, and the unwanted need for a DISTINCT.)
SELECT
t1.*
FROM
table1 t1
LEFT JOIN
table2 t2
ON t1.id = t2.t1_id
LEFT JOIN
table3 t3
ON t1.id = t3.t1_id
WHERE
t2.t1_id IS NOT NULL
OR
t3.t1_id IS NOT NULL

SQL query to have data in field instead of new record

I have searched for this but I'm probably using the wrong terminology.
The following query
SELECT t1.name, t2.entry FROM t1
INNER JOIN t2 ON t2.ID = t1.ID
WHERE t2.meta_key IN ('wp_x1','wp_x2');
Returns data similar to below where there are 2 records for each of the meta_key fields
name1,wp_x1_entry
name1,wp_x2_entry
name2,wp_x1_entry
name2,wp_x2_entry
How do I amend the query to return this instead?
name1,wp_x1_entry,wp_x2_entry
name2,wp_x1_entry,wp_x2_entry
The table/field names have been changed to hide sensitive info. Also, I know these are badly designed tables but I am unable to change the db structure.
This will be calling a mySql db from C# code.
Join another time with t2, looking for wp_x2 only
SELECT t1.name, t2.entry, t3.entry
FROM t1
JOIN t2 ON t2.ID = t1.ID and t2.meta_key = 'wp_x1'
JOIN t2 as t3 ON t3.ID = t1.ID and t3.meta_key = 'wp_x2'
Will return only rows with both wp_x1 and wp_x2. Use LEFT_JOIN if one/none is required.
Try to Group By t1.name and group_concat t2.entry.
like this :
SELECT t1.name, GROUP_CONCAT(t2.entry) FROM t1
INNER JOIN t2 ON t2.ID = t1.ID
WHERE t2.meta_key IN ('wp_x1','wp_x2')
GROUP BY t1.name;
http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html

UPDATE with JOIN and GROUP_CONCAT

I have 4 tables, one of which I need to update.
Table t1 needs to be updated according to the information in table t2 (t1.id = t2.id)
Table t2 contains information about websites (e.g.ID, traffic ).
Table t3 is a m:n table, that links the IDs in table t2 with the languages in table t4 based on language codes (ISO2) (e.g. XID: 1 | ISO2: EN,DE,FR)
Table t4 contains the ISO2-Codes (e.g. EN, DE, FR) and the respective languages (English, German, French)
Now I need to update the languages column in table t1 based on the information in tables t2,t3,t4.
I have written the following query, but it says SQL Error (1111): Invalid use of group function */
UPDATE t1
LEFT JOIN t2
ON t1.id = t2.id
LEFT JOIN t3
ON t2.id = t3.X_id
LEFT JOIN t4
ON t3.languages_iso2 = t4.iso2
SET t1.languages = GROUP_CONCAT(t4.`language` ORDER BY t4.language ASC)
I know that this solution can't be the most elegant one, but my SQL skills are not that good, so I don't know what else I should try. Does anyone have a solution for this problem?
Thanks in advance!
Try this:
UPDATE t1
INNER JOIN (SELECT t2.id, GROUP_CONCAT(t4.language ORDER BY t4.language) languages
FROM t2
INNER JOIN t3 ON t2.id = t3.X_id
INNER JOIN t4 ON t3.languages_iso2 = t4.iso2
GROUP BY t2.id
) AS t2 ON t1.id = t2.id
SET t1.languages = t2.languages;

mySQL Nesting of joins N-1 and N-N

I'm trying to join 4 tables, two 1-N ones and one through an N-N table. Strangely mySql doesn't seem to like one of my syntaxes. Does anybody know if this is due to myOwn limitations or mySql?
This doesn't work:
SELECT *
FROM tableOne t1 JOIN tableTwo t2
LEFT OUTER JOIN N_N_tableThree t3
JOIN tableFour t4 ON t4.id = t3.fk_tableFour
ON t2.id = t3.fk_tableTwo
ON t2.id = t1.fk_tableTwo
While this does work
SELECT *
FROM tableOne t1,
tableTwo t2 LEFT OUTER JOIN N_N_tableThree t3
JOIN tableFour t4 ON t4.id = t3.tableFour_id
ON t2.id = t3.tableTwo_id
WHERE t2.id = t1.tableTwo_id
Anybody a clue?
Thanks for answering.
Use this syntax instead:
SELECT *
FROM tableOne t1
INNER JOIN tableTwo t2 ON t2.id = t1.fk_tableTwo
LEFT JOIN N_N_tableThree t3 ON t2.id = t3.fk_tableTwo
INNER JOIN tableFour t4 ON t4.id = t3.fk_tableFour;
This will be equivalent of the second query that worked.
Because the WHERE t2.id = t1.tableTwo_id in the second query is actually an INNER JOIN1, which will be the same as INNER JOIN tableTwo t2 ON t2.id = t1.fk_tableTwo as I did. This is the old JOIN syntax, try to avoid it and use the ANSI SQL-92 syntax instead as I did. For more information see this:
Bad habits to kick : using old-style JOINs.
The query you posted didn't work, because it is not the correct syntax for JOIN in MySQL, you have three ON clauses after each others:
...
ON t4.id = t3.fk_tableFour
ON t2.id = t3.fk_tableTwo
ON t2.id = t1.fk_tableTwo
Each JOIN should has the join condition specified with the ON clause after the JOIN directly, if not it would a cross join2. But not multiple ON's the way you did.
SQL Fiddle Demo
1: Don't be confused with the use of INNER JOIN instead of JOIN they are the same the default JOIN is inner join, I just I used for readability. Also, the same thing with the OUTER keyword, I omit it in LEFT JOIN since it is optional when using LEFT or RIGHT
2: You will find other variations of the JOIN syntax in MySQL in the reference page, like the JOIN tablename without a join condition, and others. You might need to read them.

SQL join ON not equal in Mysql

I have two tables. Both contains question id field. I want to get all records from first table that are not present in second one. I don't want to use "NOT IN" constrain as second table having more than 400000 records.
Try something like
SELECt t1.*
FROM Table1 t1 LEFT JOIN
Table2 t2 ON t1.questionID = t2.questionID
WHERE t2.questionID IS NULL
Typically you would do this using a LEFT JOIN combined with a WHERE clause selecting every row where the joined table returns no results.
SELECT t1.*
FROM Table1 t1
LEFT OUTER JOIN Table2 t2 ON t2.ID = t1.ID
WHERE t2.ID IS NULL
try:
select from t1
right join t2 on t2.id = t1.id where t2.id is null