MYSQL Performance Tuning with group by clause - mysql

I have a query like this.
SELECT count(*)
FROM table1 e
WHERE e.column1=1
AND e.id IN
(SELECT MAX(ID)
FROM table2 A
WHERE A.column1=1
AND A.date=CURDATE()
GROUP BY A.column2);
When I run this query it is taking too much of time as I am having thousands of records. How can I tune the query to perform better.
Thanks in advance.
EDIT: column2 in table2 is id of Table1

Change in (. . .) To use join instead. Like
SELECT count(*)
FROM table1 AS e
Inner join
(
SELECT MAX(ID)
FROM table2 A
WHERE A.column1 = 1
AND A.date = CURDATE()
GROUP BY A.column2
) t2 on e.id = t2.id
WHERE e.column1 = 1

Maybe:
SELECT count(*)
FROM table1 e
WHERE e.column1=1
AND EXISTS
(SELECT *
FROM table2 A
WHERE A.column1=1
AND A.date=CURDATE()
AND A.ID = e.id);

Related

Join with column outside subquery

SELECT t1.name as r_name, t1.values as r_values
FROM table as t1
JOIN (
SELECT SUM(amount) as amount
FROM database2.table
WHERE ids IN (t1.values)
) as t2
WHERE t1.id = 20;
I get an error, that t1.values inside the subquery is unknown column.
You need to rewrite your query and take inne where to join condition:
SELECT t1.name as r_name, t1.values as r_values
FROM table as t1
JOIN (
SELECT SUM(amount) as amount
FROM database2.table
) as t2 ON t2.ids = t1.values
WHERE t1.id = 20;
Also, you don't use amount column, so what is the point of join?
Another issue, you don't have any join condition defined.
I think you need to read about joins in SQL first :)
It seems you are trying to join database2.table to your t1 based on t1.values list.
I added group by IDs in t2 since your using aggregation function. Then, not sure what's the purpose of your sum(amount)
SELECT t1.name as r_name, t1.values as r_values
FROM table as t1
JOIN (
SELECT SUM(amount) as amount, ids
FROM database2.table
GROUP BY ids
) as t2 on t2.ids IN (t1.values)
WHERE t1.id = 20;

Get id of the record having Min() value

I have a complex mysql query where one of the Select fields is Min(value). Since all the 'values' are unique, is there also a way to get found min value's row id along?
In other words if we simplify the query to this question, it is like this:
SELECT t1.name, MIN(t2.value) AS minval
FROM table1 t1
LEFT JOIN table2 t2
ON t2.id_user = t1.id
GROUP BY id_user
How can i now know which t2.id was chosen for lowest t2.value for particular user? Thank you!
Use ROW_NUMBER() to find the first value of each id_user
You can replace * with the fields you need
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY t2.id_user ORDER BY t2.value) as rnk
FROM table1 t1
LEFT JOIN table2 t2
ON t2.id_user = t1.id
) as X
WHERE X.rnk = 1
Maybe this simple, dont know how complex your statement is:
SELECT name,value,id
FROM(
SELECT t1.name,t2.value,t2.id
FROM table1 t1
LEFT JOIN table2 t2
ON t2.id_user = t1.id
GROUP BY t2.id,id_user
ORDER BY t1.name,t2.id asc) as test
GROUP BY name;

How to use 'group by' while having a sub query in MySQL?

I have the following query:
SELECT c.text1, sum(c.num1), sum(c.num2), sum(c.num3),
(SELECT count(id) FROM table2 WHERE type = 1 AND txt = c.text1 AND spec_id = c.sp_id)
FROM table1 as c
WHERE c.type = 1
GROUP BY c.text1, c.sp_id
Is there a workaround to loose the c.sp_id from the GroupBy clause somehow? I know that if I remove it MySQL will return an error.
Or is there a way to group the results of this query by c.text1 only?
If I understand the problem correctly, you need to do two separate aggregations. This is one version of the query:
SELECT c.text1, c.sum1, c.sum2, c.sum3, t2.cnt
FROM (SELECT c.text1, sum(c.num1) as sum1, sum(c.num2) as cum2, sum(c.num3) as sum3
FROM table1 c
GROUP BY c.text1
) c LEFT JOIN
(SELECT txt, count(*) as cnt
FROM table2 t2
WHERE t2.type = 1 AND
EXISTS (SELECT 1
FROM table1 c2
WHERE t.txt = c2.txt AND c2.type = 1 AND
t.spec_id = c2.sp_id
)
) t2
ON t2.txt = c.text1
WHERE c.type = 1;

MySQL (pdo) Slow sub query

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;

MySql subquery return more than 1 record

I have a MySQL query:
SELECT *
FROM t1
WHERE ABS(TIMESTAMPDIFF(MINUTE, ts, (SELECT ts FROM t1 WHERE id=1 AND c1 < 5))) < 3
AND id=1
The subquery returns 4 rows so the query cannot executed. I have change it to use join:
SELECT *
FROM t1 a INNER JOIN t1 b
ON a.id=b.id AND b.id=1 AND c1<5
WHERE ABS(TIMESTAMP(MINUTE, a.ts, b.ts))<3;
I have 2 questions:
1. Did I convert (from subquery to join) right?
2. Anyway to fix my subquery?
Thanks.
1.
It's not valid, as you have to prefix all your fields (maybe a typo), or you'll have ambiguity errors. And other typos (TIMESTAMP instead of TIMESTAMPDIFF)
By the way, in your case, you can put the predicate condition in the where clause, not in the join.
SELECT a.*
FROM t1 a
INNER JOIN t1 b
ON a.id=b.id
WHERE a.id = 1
AND b.c1 < 5
AND ABS(TIMESTAMPDIFF(MINUTE, a.ts, b.ts))<3;
2.
Maybe an EXISTS clause could do the job.
SELECT a.*
FROM t1 a
WHERE a.id = 1
AND EXISTS
(SELECT NULL FROM t1 b
WHERE b.id = a.id
AND b.c1 < 5
AND TIMESTAMPDIFF(MINUTE, a.ts, b.ts) < 3)
you can use distinct key in select query like this
SELECT distinct a.* FROM t1 a WHERE a.id = 1 AND EXISTS (SELECT NULL FROM t1 b WHERE b.id = a.id AND b.c1 < 5
AND TIMESTAMPDIFF(MINUTE, a.ts, b.ts) < 3)