MySQL (pdo) Slow sub query - mysql

I have a query that gets website visit logs from a table. I then join a further 2 tables to this in order to get all of the user's information. At this point the query is fine.
Slow Query:
I then have to use a sub query in order to get the visit logs as of a certain date based on a condition. It is this sub query that causes the entire query to practically grind to a halt. After 30-40 seconds the query will finish.
Tables:
The tables are InnoDB (changing to MyISAM made no difference to performance).
Table1 has close to 1m records. Table2 has around 250K. Table3 has around 100K. Table4 has around 500K.
Query:
SELECT COUNT(*) AS visits , table1.userName, table2.userId, table2.col1, table2.col2, table2.col3, table2.col4, table3.col20
FROM table1
LEFT JOIN table2 ON table2.userName = table1.userName
LEFT JOIN table3 ON table3.col2 = table1.col2
WHERE table1.col1 = 'foo'
AND table1.Date > (
SELECT max(a.VisitDate) FROM table4 a
WHERE a.userId = table2.userId AND a.col1 = 'bar'
)
GROUP BY table1.userName, table1.Date
I did not build this data structure and have no option to change it. I am parsing this query into a basic PDO function.

You can try using a inner join (on subselect)
SELECT
COUNT(*) AS visits
, table1.userName
, table2.userId
, table2.col1
, table2.col2
, table2.col3
, table2.col4
, table3.col20
FROM table1
LEFT JOIN table2 ON table2.userName = table1.userName
LEFT JOIN table3 ON table3.col2 = table1.col2
INNER JOIN (
SELECT max(a.VisitDate) as max_visit_date FROM table4 a
WHERE a.userId = table2.userId AND a.col1 = 'bar'
) t on table1.Date > t.max_visit_date
GROUP BY table1.userName, table1.Date;

Related

INNER JOIN with more than one OR operators in WHERE clause increases the execution time

as we know - "INNER JOIN with complex condition dramatically increases the execution time please refer this"
consider the query
(
SELECT ...
FROM Table1
INNER JOIN Table2 ON Table1.P1 = Table2.P1 OR Table1.P2 = Table2.P2
)
Over here comparison will be done via "nested loops" so execution time will be more but if we have a query like-
(
SELECT ...
FROM Table1
INNER JOIN Table2 ON Table1.P3 = Table2.P3 where Table1.P1 = "abc" OR
Table2.p2 = "xyz"
)
or like-
(
SELECT ...
FROM Table1
INNER JOIN Table2 ON Table1.P3 = Table2.P3 where Table1.P1 LIKE "abc" OR
Table2.p2 LIKE "xyz"
)
than also does the comparison will take place through nested loops only (for columns P1 ANd P2)?
Please Use Union instead of 'OR' condition in JOIN.
SELECT ...
FROM Table1
INNER JOIN Table2 ON Table1.P1 = Table2.P1
UNION All
SELECT ...
FROM Table1
INNER JOIN Table2 ON Table1.P2 = Table2.P2 AND Table1.P1 <> Table2.P1

How to LEFT JOIN table1 ON table2 WHERE table2 row fulfills certain conditions

Please consider this query:
SELECT table1.* ,
(SELECT quantity FROM table2 WHERE id = table1.id AND table2.location = 10) quantity,
(SELECT reorder_level FROM table2 WHERE id = table1.id AND table2.location = 10) reorder_level,
(SELECT stock_date FROM table2 WHERE id = table1.id AND table2.location = 10) stock_date
FROM table1
WHERE category_id = 5 ORDER BY table1.id;
The aliases quantity, location and stock_date are obviously referencing a a row in table2 that fulfill the condition: id=table1.id and location=10.
This query works, but is probably suboptimal as a result of the clumsy subqueries.
How can I best join table1 to table2 USING(id) but only on rows where location is also 10.
TIP: One row from table1 has many rows in table2.
Unfortunately, the actual table definitions are much more complex, and I reckoned it might be counter-productive to dump the entire thing on this thread.
You can use additional condition in ON() part so it will join only rows which fulfills the provided criteria
SELECT t1.* ,
t2.quantity ,
t2.reorder_level,
t2.stock_date
FROM table1 t1
LEFT JOIN table2 t2 ON t1.id = t2.id AND t2.location = 10
WHERE t1.category_id = 5
ORDER BY t1.id;
Another way would be use a subselect for your table2 and select only rows where location is equal to 10
SELECT t1.* ,
t2.quantity ,
t2.reorder_level,
t2.stock_date
FROM table1 t1
LEFT JOIN
(SELECT * FROM table2 WHERE t2.location = 10) t2
ON t1.id = t2.id
WHERE t1.category_id = 5
ORDER BY t1.id;

How to optimize IN constraint query?

Following is my query.
SELECT * FROM t1 WHERE t1.record_id IN (
SELECT t2.record_id FROM t2
INNER JOIN t3 ON CONCAT(t2.case_number,t2.courtfile_type) = CONCAT(t3.case_number,t3.courtfile_type))
It contain IN constraint which is taking lot of time to extract result from database.Database is huge obviously.
How can I optimize this query?
Try this:
USING JOIN
SELECT DISTINCT t1.*
FROM t1
INNER JOIN t2 ON t1.record_id = t2.record_id
INNER JOIN t3 ON t2.case_number = t3.case_number AND t2.courtfile_type = t3.courtfile_type
USING EXISTS
SELECT *
FROM t1
WHERE EXISTS (SELECT t2.record_id
FROM t2 INNER JOIN t3 ON t2.case_number = t3.case_number AND t2.courtfile_type = t3.courtfile_type
WHERE t1.record_id = t2.record_id )
Check the execution plan of the query using EXPLAIN keyword and do proper indexing on tables.

Is is possible to simplify this SQL UNION query?

Is is possible to simplify this UNION to avoid the near redundancy of the queries being unioned? As seen here, both queries are similar. They just join on a different column in table2. The reason i use Union, instead of just Inner Joining 2x in the same query is because the results must be in 1 column by virtue of the fact that this queries is used as a subquery.
SELECT t1.id as id
FROM table1 g
INNER JOIN table2 t1 on g.t_id = t1.id
WHERE g.id=1
UNION
SELECT t2.id as id2
FROM table1 g
INNER JOIN table2 t2 on g.t2_id = t2.id
WHERE g.id=1
I don't see why this couldn't be treated as a simple inner join that can be satisfied by a match in either of two predicates. I've removed the original table aliases of t1, t2, and g for the sake of clarity. Since I don't know if the query could produce duplicate rows, I used DISTINCT in order to collapse duplicate rows in the same manner that the UNION did in the original query.
SELECT DISTINCT table2.id
FROM table1
INNER JOIN table2
ON ( table1.t_id = table2.id OR table1.t2_id = table2.id )
WHERE table1.id = 1
;
It is possible to do with two joins, and the IFNULL() function:
SELECT IFNULL (t1.id, t2.id) as id
FROM table1 g
INNER JOIN table2 t1 on g.t_id = t1.id
INNER JOIN table2 t2 on g.t2_id = t2.id
WHERE g.id=1
You might find this simpler:
select distinct t.id
from table2 t
where t.id in (select g.t_id from table1 g) or
t.id in (select g.t2_id from table1 g)
However, the performance would be awful on MySQL. You can also do:
select distinct t.id
from table2 t
where exists (select 1 from table1 g where g.t_id = t.id or g.t2_id = t.id)
The second version should work better in MySQL.

mysql select multiple and join query

I have a complicated MYSQL query question here. I try my best to explain my problem.
I have 4 tables. mid is a foreign key between the tables. table4 is NOT a compulsory table source. However I like it returns all the rows even there are no match data from table4. So that I write a query script as following.
I'm not sure is it the logic way to write such query script but what I know that the syntax is wrong.
SELECT *
FROM table1, table2, table3,
(SELECT xx
FROM table4
RIGHT JOIN table1 ON table1.mid = table4.mid)
WHERE table1.mid = table2.mid
AND table1.mid = table3.mid
AND tt = 'a'
AND type = 1
GROUP BY table1.mid
ORDER BY xx DESC, table1.name ASC;
You have to do a left join between table1 & table4:
SELECT *
FROM table1
JOIN table2 ON table1.mid = table2.mid
JOIN table3 ON table1.mid = table3.mid
LEFT JOIN table4 ON table1.mid = table4.mid
WHERE tt = 'a'
AND type = 1
GROUP BY table1.mid
ORDER BY xx DESC, table1.name ASC;