Optimizing a query : joining a table on itself - mysql

i want to get last two row of a table in one query as new data and previous data
i got
select tbl.x , tbl2.x as last_x
from tbl left join tbl tbl2 ON tbl.id!= tbl2.id
order by tbl.id desc , tbl2.id desc limit 1
it works fine but i think it might get slow in a big DB
is there any way to make this faster ?

A LIMIT should work in a basic subquery, and so the following will possibly be more efficient
SELECT Sub1.x , Sub2.x as last_x
FROM (SELECT x FROM tbl ORDER BY tbl.id DESC LIMIT 1) Sub1
CROSS JOIN (SELECT x FROM tbl ORDER BY tbl.id DESC LIMIT 2, 1) Sub2

You can take a look at the execution plan and try to optimize your query, but usually you do this when you face a problem so you can determine which parts are taking long.
Chick this thread to: How to optimise MySQL queries based on EXPLAIN plan
But as saied i would not try to solve a problm which still does not exist, i do not actually see a problem aith your query.

Related

Why is my sql query with a subquery so slow even though the subquery performs fast?

My query is something like:
SELECT *,
(SELECT COUNT(*) FROM comments WHERE comments.thread = threads.id) AS comments
FROM threads
LIMIT 10
comments.thread is an index, queries like this run fast:
SELECT COUNT(*) FROM comments WHERE comments.thread = 'someId'
However, my query is extremly slow. It takes 10 seconds times the limit I define. Why?
For this query:
SELECT t.*,
(SELECT COUNT(*) FROM comments c WHERE c.thread = t.id) AS comments
FROM threads t
LIMIT 10;
You want an index on comments(thread). If your other query runs fast, then I would guess that you already have one.
Perhaps the LIMIT and subquery are acting strangely. Is this version also slow?
SELECT t.*,
(SELECT COUNT(*) FROM comments c WHERE c.thread = t.id) AS comments
FROM (SELECT t.*
FROM threads t
LIMIT 10
) t;
Your inner query is a corelated subquery, meaning it uses a value from the outer query, so executes for every row of the outer query. Maybe MySQL is not so good at optimizing the query.
Try this:
SELECT threads.*, count(comments.thread) AS comments
FROM threads
JOIN comments ON comments.thread = threads.id
GROUP BY 1,2,3,4,5 -- one number here for each column of the threads table
LIMIT 10

mysql result with order by is different from order by and limit

I have a table A with columns id, age.
Two queries below return different result and I don't know why:
SELECT distinct a.id id FROM A a ORDER BY a.age DESC
SELECT distinct a.id id FROM A a ORDER BY a.age DESC LIMIT 10 OFFSET 0
Any ideas? I would expect the second query to return the first 10 results of the first query.
EDIT:
What I forgot to say is that there are rows with the same age. So I think it has something to do with it.
I am surprised they work. In most databases you would get a syntax error, because a.age is not in the select. I know that MySQL extends the GROUP BY to allow the use of un-aggregated columns. I presume it has the same reasoning for SELECT DISTINCT. That is, age comes from an indeterminate row for each id. It is so indeterminate that it might change from one query to another.
Here are two appropriate ways to write the query:
SELECT distinct a.id, a.age
FROM A a
ORDER BY a.age DESC;
or:
SELECT a.id
FROM A a
ORDER BY MIN(a.age) DESC; -- Or perhaps MAX(a.age) or AVG(a.age)
These should have more stable results, with or without the LIMIT.
Found a solution.
I'm not sure why it's happening. I'm using mysql and maybe the implementation of the query when adding limit is different from when without it.
Any way, I added in the ORDER BY a.id. This kept the order when adding limit.
So this is how the query looks:
SELECT distinct a.id id FROM A a ORDER BY a.age DESC, a.id LIMIT 10 OFFSET 0

Optimizing Query with UNION ALL and ORDER BY

I have 3 tables (ex. a,b,c) which indicates activities for different items (ex. commenting, liking, etc) as well as the time for each activity. I am trying to essentially do a sort of news feed that shows the most recent activities first. I constructed a UNION ALL for all three tables to group all the activities together and then a GROUP BY to ensure that activities for the same items are not shown twice and order by time DESC. This function uses an infinite scroll so the query must also be able to shift appropriately.
I am wondering if there is any way to optimize this (Each table is about 500-900K and growing). Truncated code is shown below.
SELECT time,item_id FROM (
SELECT a.time AS time, a.item_id FROM a
UNION ALL
SELECT b.time AS time, b.item_id FROM b
UNION ALL
SELECT c.time AS time, c.item_id FROM c
) temp
GROUP BY item_id
ORDER BY time DESC
LIMIT 10
The query you've written will create a very large temporary table. You're then sorting by a column in that temporary table. You should try to limit each table, perhaps like this:
SELECT time,item_id FROM (
SELECT a.time AS time, a.item_id FROM a LIMIT 10 ORDER BY time DESC
UNION ALL
SELECT b.time AS time, b.item_id FROM b LIMIT 10 ORDER BY time DESC
UNION ALL
SELECT c.time AS time, c.item_id FROM c LIMIT 10 ORDER BY time DESC
) temp
GROUP BY item_id
ORDER BY time DESC
LIMIT 10
You'll want to make sure time has an index on each table.
I don't really like doing this though, as it may be difficult to "scroll" through the results accurately.
When going to the "next page" you may want to consider adding a WHERE clause like WHERE a/b/c.item_id > num instead of LIMIT offset, length. That will help with the accuracy.
When writing the query you should prefix the query with EXPLAIN to see how the query is being handled. This will give you a better idea of what's happening: Are temporary tables being created? How large is it? What indexes are being used? etc...
Another approach could be to use a MySQL trigger to populate a single "feed" table.

mySQL index and preparing state

I get a complicate query:
SELECT * FROM
(
SELECT Transaction
FROM table1
WHERE
Transaction IN (SELECT Transaction FROM table2 WHERE Plugin='XXX' AND Server='XXX')
AND
Transaction NOT IN (SELECT Transaction FROM table1 WHERE Detail IN ('Monitor','Version','monitor','version'))
ORDER BY Date DESC, Millisecond DESC LIMIT 10)
AS res
I get indexes on table1:Detail and the "Transaction" is the primary key of table2.
It will take a while(5-10 secs) for the database to return result. So I create another index on table2:Plugin, the query is fasted now, but a preparing state shows up and also takes 5-10 secs. So after I create a new index, the time does not change at all.
Can someone tell me what`s going on and how can I optimize this query? Thank you!
Could you not simply rewrite the query as follows:
SELECT a.Transaction
FROM table1 a
INNER JOIN table2 b ON b.Transaction = a.Transaction
WHERE (b.Plugin='XXX' AND b.Server='XXX')
AND a.Detail NOT IN ('Monitor','Version','monitor','version')
ORDER BY a.Date DESC, a.Millisecond DESC LIMIT 10
So you join the table2 (which will be faster) and remove all the subqueries.
This should be much faster.

nested query on the same table

do you think a query like this will create problem in the execution of my software?
I need to delete the all the table, except the last 2 groups of entries, grouped by the same time of insert.
delete from tableA WHERE time not in
(
SELECT time FROM
(select distinct time from tableA order by time desc limit 2
) AS tmptable
);
Do you have better solution? I'm using mysql 5.5
I don't see anything wrong with your query, but I prefer using an OUTER JOIN/NULL check (plus it alleviates the need for one of the nested subqueries):
delete a
from tableA a
left join
(
select distinct time
from tableA
order by time desc
limit 2
) b on a.time = b.time
where b.time is null
SQL Fiddle Demo