So I've got massive slow SQL query and I've narrowed it down to a slow sub-query, so I want to rewrite it to a JOIN. But I'm stuck... (due to the MAX and GROUP BY)
SELECT *
FROM local.advice AS aa
LEFT JOIN webdb.account AS oa ON oa.shortname = aa.shortname
WHERE aa.aa_id = ANY (SELECT MAX(dup.aa_id)
FROM local.advice AS dup
GROUP BY dup.shortname)
AND oa.cat LIKE '111'
ORDER BY aa.ram, aa.cpu DESC
LIMIT 0, 30
Here is a different version of your query where the subquery is converted with a join clause
select * from local.advice aa
JOIN webdb.account oa ON oa.shortname = aa.shortname
join(
select max(aa_id) as aa_id,shortname from local.advice
group by shortname
)x on x.aa_id = aa.aa_id
where
oa.cat = '111'
order by aa.ram, aa.cpu DESC
limit 0,30
Also you may need to apply indexes if they are not added already
alter table local.advice add index shortname_idx(shortname);
alter table webdb.account add index cat_shortname_idx(cat,shortname);
alter table local.advice add index ram_idx(ram);
alter table local.advice add index cpu_idx(cpu);
I am assuming aa_id is a primary key so did not add the index
Make sure to take a backup of the tables before applying the indexes
Related
I am looking to optimize below query which has a subquery from relation table and has a order by on subquery count data. Please see the below query:
SELECT table1.*,
( SELECT COUNT(*)
FROM table2
WHERE table2.user_id=table1.id
AND table2.deleted = 0) AS table2_total
FROM table1
WHERE table1.parent_id = 0
ORDER BY table2_total DESC LIMIT 0, 50
This query works well but it stuck when table2 has more than 50K data. I have also tried to use left join instead of sub query but that is even more slower:
SELECT table1.*,
COUNT(DISTINCT table2.id) as table2_total
FROM table1
LEFT JOIN table2 ON table2.user_id=table1.id
AND table2.deleted = 0
WHERE table1.parent_id = 0
ORDER BY table2_total DESC LIMIT 0, 50
table2 already has indexes on user_id and deleted column. Please see below table2 structure:
Is there any way to optimize this query in better way?
As written, it will go through the entirety of table1, and probe table2 that many times.
Add this composite index to table2: INDEX(user_id, deleted) and remove the INDEX(user_id) that you currently seem to have.
You can try to add index to the column table2.deleted And table1.parent_id. The index is going to impact the performance of the insert .
I'm trying to do a query in mysql
SELECT *
FROM table_name1
WHERE codregister IN
(SELECT register
FROM tablename2
WHERE city LIKE '%paris%')
ORDER BY date DESC
In table_name "codregister" is a primary key but in tablename2 "register" field column is a index (primary key on tablename2 is an autoincrement).
In the table_name1 doesn´t have matches with the tablename2 but the execution time of the query is to slow. Someone can recommend to improve the query?
Check this considerable difference from csf answer
SELECT *
FROM
table_a a
INNER JOIN
(SELECT register
FROM table_b
WHERE city like '%paris%') b
ON(a.codregister=b.register)
ORDER BY a.date
You're only projecting the only field of B that you need, and you are filtering B records before the join.
Use JOIN instead , and make sure joining variables are indexed .
SELECT * FROM table_name1 a
JOIN tablename2 b on a.codregister = b.register
where b.city like '%paris%'
ORDER BY a.date DESC
Also for any query you write try to use 'EXPLAIN' to find more about your query
Ref :
http://dev.mysql.com/doc/refman/5.0/en/explain.html
i agree with 0R10N Answer , filtering before join can make join faster . ThankI
I have an sql query to get values from 4 tables. In my query takes lot of time. I need to simplify the query
what i need is i have to display only 50 records. In my table i have 90,000 records. so i deciede to apply batch processing like
first select the 50 records from first table and then check with the 3 other tables.
if the 50 is satisfied i will display that otherwise i have to continue next 50.
But i don't have idea to implement
select file_name,
A.id,
A.reference,
user.username,
c.update_date
from A_Table A,
(select reference
from B_Table
where code = 'xxx'
group by reference
having count(*) > 1) B,
C_Table c,
D_Table d
where A.reference = B.reference
and A.id = c.id
and A.code = 'ICG'
and c.updated_by = d.user_id
order by 3
limit 20;
The query looks fine.
Adding some indexes will help a lot.
Assuming the id columns (A_Table.id and C_Table.id) are already PRIMARY KEY columns, you won't need to index them.
ALTER TABLE A_Table
ADD INDEX (reference),
ADD INDEX (code);
ALTER TABLE B_Table
ADD INDEX (reference),
ADD INDEX (code, reference);
ALTER TABLE C_Table
ADD INDEX (updated_by);
ALTER TABLE D_Table
ADD INDEX (user_id);
I'm wondering why statements like select * as t appear in mysql subqueries like the following.
The following deletes the oldest 3 rows in a table according to a created_time column.
Why is this right
DELETE FROM mytable WHERE id = ANY
( SELECT * FROM ( SELECT id FROM mytable ORDER BY created_time ASC LIMIT 3')as t)
and not
DELETE FROM mytable WHERE id = ANY
(SELECT id FROM mytable ORDER BY created_time ASC LIMIT 3)
?
To me, the second form makes sense. It doesn't work and I'd like to understand why the first is necessary. Specifically, what is t and what does as t do?
In many databases, a subquery in a from clause needs to have an explicit alias. The as is optional. I typically use as for columns and leave it out for tables:
DELETE FROM mytable
WHERE id = ANY ( SELECT * FROM ( SELECT id FROM mytable ORDER BY created_time ASC LIMIT 3') t)
Why you need the subquery is a vagary of MySQL. It doesn't allow the table reference in a delete or update to appear in a subquery clause. Oh, it does allow it in a subquery-within-a-subquery clause. So, it is pretty easy to work around this limitation.
I have a table taged with two fields sesskey (varchar32 , index) and products (int11), now I have to delete all rows that having group by sesskey count(*) = 1.
I'm trying a fews methods but all fails.
Example:
delete from taged where sesskey in (select sesskey from taged group by sesskey having count(*) = 1)
The sesskey field could not be a primary key because its repeated.
DELETE si
FROM t_session si
JOIN (
SELECT sesskey
FROM t_session so
GROUP BY
sesskey
HAVING COUNT(*) = 1
) q
ON q.sesskey = si.sesskey
You need to have a join here. Using a correlated subquery won't work.
See this article in my blog for more detail:
Keeping rows
Or if you're using an older (pre 4.1) version of MySQL and don't have access to subqueries you need to select your data into a table, then join that table with the original:
CREATE TABLE delete_me_table (sesskey varchar32, cur_total int);
INSERT INTO delete_me_table SELECT sesskey, count(*) as cur_total FROM orig_table
WHERE cur_total = 1 GROUP BY sesskey;
DELETE FROM orig_table INNER JOIN delete_me_table USING (sesskey);
Now you have a table left over named delete_me_table which contains a history of all the rows you deleted. You can use this for archiving, trending, other fun and unusual things to surprise yourself with.
The SubQuery should work
Delete from taged
Where sesskey in
(Select sesskey
From taged
Group by sesskey
Having count(*) = 1)
EDIT: Thanks to #Quassnoi comment below... The above will NOT work in MySql, as MySql restricts referencing the table being updated or deleted from, in a Subquery i you must do the same thing using a Join ...