Hi I like to make a perfomant order by query.
This is my query:
SELECT a.status,a.title,a.id,
b.deployed,b.accepted,b.rejected,b.error
FROM a
LEFT JOIN b ON ( a.id = b.id )
WHERE b.agencyid = 1 AND a.userid = 3
ORDER BY a.date DESC
LIMIT 0,50
without the ORDER BY clause the query takes 0.006 s
with the ORDER BY clause it needs 1.1181 s !!!
The most time consumption comes from Copying To Tmp Table 1,1 s 99,78% 1 1,1 s
How I can decrease the Copy to tpm table time?
First, as noted by Strawberry, because you have the "b" alias referenced in the where clause, and not considering NULL, it in essence turns your left join to an INNER JOIN.
SELECT
a.status,
a.title,
a.id,
b.deployed,
b.accepted,
b.rejected,
b.error
FROM a JOIN b
ON a.id = b.id
WHERE
a.userid = 3
AND b.agencyid = 1
ORDER BY
a.date DESC
LIMIT
0,50
If you meant for it to be a LEFT-JOIN (all "a" records regardless of a match in the "b" table), then you would need to move your "b" qualifier to the JOIN clause.
SELECT
a.status,
a.title,
a.id,
b.deployed,
b.accepted,
b.rejected,
b.error
FROM a LEFT JOIN b
ON a.id = b.id
AND b.agencyid = 1
WHERE
a.userid = 3
ORDER BY
a.date DESC
LIMIT
0,50
That being said, you are looking for a specific user ordered by date. I would have your "a" table indexed on (userid, date) so the engine can be optimized on your WHERE qualifier AND the order by and should resolve your performance issue regardless of left and inner join...
To help the join condition to your "b" table, index it on (id, agencyid)
Related
I am working with a query, which looks like this
SELECT s.c1, s.t, s.u, s.dt, t.temp, t.dt AS dt2
FROM `systemusage` AS s
INNER JOIN temperature AS t ON s.did=t.did
WHERE t.did = (SELECT id FROM devices WHERE m = 1)
LIMIT 1
Which works just fine, however if I add ORDER BY s.id, then the query gets totally stuck, can someone guide me on why? the id field is primary, so it should be indexed no?
Add an index on the column temperature.did so that the WHERE clause can be implemented efficiently.
It also may help to replace WHERE t.did = (SELECT ...) with a JOIN.
SELECT s.c1, s.t, s.u, t.temp
FROM `systemusage` AS s
INNER JOIN temperature AS t ON s.did=t.did
INNER JOIN devices AS d ON d.id = t.did
WHERE d.m = 1
ORDER BY s.id DESC, t.id DESC
LIMIT 1
Here is three table, order, order_record, pay, with near 2300000 records.
there will be more than 1 record in pay table when giving 1 order_id, so I need to use DISTINCT to remove repeated result
now I need to get distinct data from those three table join on order_id, the example query sql below:
SELECT
DISTINCT (a.order_id)
a.order_id,a.user_id
b.boss_order_id,
c.pay_id,
FROM order a
LEFT JOIN order_record b ON a.order_id = b.order_id AND b.is_delete IN (0,1)
LEFT JOIN pay c ON a.order_id = c.order_id AND c.is_delete =0 WHERE 1=1 AND a.is_delete IN (0,1)
ORDER BY a.id DESC LIMIT 0, 10
this query will takes plenty of time.
then I change to use "GROUP BY":
SELECT
a.order_id,a.user_id
b.boss_order_id,
c.pay_id,
FROM order a
LEFT JOIN order_record b ON a.order_id = b.order_id AND b.is_delete IN (0,1)
LEFT JOIN pay c ON a.order_id = c.order_id AND c.is_delete =0 WHERE 1=1 AND a.is_delete IN (0,1)
GROUP BY a.order_id
ORDER BY a.id DESC LIMIT 0, 10
this time the query takes 122 seconds.
Is there any faster way to implement?
You are using a left join. Hence, you can do:
SELECT o.order_id, o.user_id, orr.boss_order_id, p.pay_id,
FROM (SELECT o.*
FROM order o
WHERE o.is_delete IN (0, 1)
ORDER BY o.id DESC
LIMIT 10
) o LEFT JOIN
order_record orr
ON o.order_id = orr.order_id AND
orr.is_delete IN (0, 1) LEFT JOIN
pay p
ON o.order_id = p.order_id AND
p.is_delete = 0
WHERE 1=1 AND o.is_delete IN (0, 1)
GROUP BY o.order_id
ORDER BY o.id DESC
LIMIT 0, 10
You are using GROUP BY incorrectly, because you have unaggregated columns in the SELECT that are not in the GROUP BY.
Another approach let a where clause do most the work:
select ...
from order
left join order_using using (order_id)
...
where
order.order_id < (select max(order_id) from orders order by order_id limit 10) ...
limit 10
The final limit 10 is weird though as you may get partial records from an order if you drop the group by. I.e. you probably want to drop it and and just put a limit orders table. With the group by means you will a random data from table b and c unless you use aggregate function to tell mysql which of the row values you want.
How to customize this query, It takes around 30 Second to take out results, total records in 'videos' table are about 0.5 million, 3 million members are present in 'Members' Table, is there any alternate Query or should i break this query in 2 select queries ?
user_id is Indexed
vid_id is Indexed
select a.ref_url , a.source , a.video_name , a.viewers , b.username ,
c.points from
members_videos a
inner join Members b on a.user_id = b.user_id
inner join rankings c on c.user_id = b.user_id
where a.cat_ids in (123,234,52,234,423,122) not in (110,99)
order by a.vid_id Desc limit 10
There are multiple factors:
Make sure members_videos, Members and rankings have index on user_id column.
Break the query; eliminate Order By clause. Get the data in temporary table.
Select the data from temporary table and put Order By Clause over there.
Alternate, try sub-query.
SELECT *
FROM
(
select a.ref_url , a.source , a.video_name , a.viewers , b.username ,
c.points from
members_videos a
inner join Members b on a.user_id = b.user_id
inner join rankings c on c.user_id = b.user_id
where a.cat_ids in (123,234,52,234,423,122) not in (110,99)
) as T
order by T.vid_id Desc limit 10
Here is MySQL:
SELECT a.id,
a.name,
a.n,
a.r,
a.pot,
a.ticket_price,
a.starting_tickets,
a.started,
a.end,
COUNT(b.id) tickets_bought
FROM current_lotteries a
JOIN lottery_tickets b ON b.lid=a.id
WHERE a.cid=1
ORDER BY started DESC LIMIT 1
In the search, if there is no row from a but there are rows in b (i.e COUNT(b.id) is not NULL) then this query returns a row with NULL values for a fields and whatever the value of COUNT(b.id) as tickets_bought. How do I modify this query so it does not return a row (num_rows = 0) if there is no result in table a?
A Snap.
Absent a GROUP BY clause, MySQL (which permits this where it would be an error in other RDBMS) is applying the aggregate group over all rows in b when it should be grouping them. Add GROUP BY a.id
SELECT a.id,
a.name,
a.n,
a.r,
a.pot,
a.ticket_price,
a.starting_tickets,
a.started,
a.end,
COUNT(b.id) tickets_bought
FROM current_lotteries a
JOIN lottery_tickets b ON b.lid=a.id
WHERE a.cid=1
GROUP BY a.id
ORDER BY started DESC LIMIT 1
The above will work in MySQL but not elsewhere. A more portable version uses a correlated subquery:
SELECT a.id,
a.name,
a.n,
a.r,
a.pot,
a.ticket_price,
a.starting_tickets,
a.started,
a.end,
b.tickets_bought
FROM current_lotteries a
/* More portable to join against a subquery which returns the count per group */
JOIN (
SELECT b.lid, COUNT(*) AS tickets_bought
FROM lottery_tickets
GROUP BY lid
) b ON a.id = b.lid
WHERE a.cid = 1
ORDER BY started DESC LIMIT 1
Try this:
SELECT a.id, a.name, a.n, a.r, a.pot, a.ticket_price,
a.starting_tickets, a.started, a.end, b.tickets_bought
FROM current_lotteries a
RIGHT JOIN (SELECT b.lid, COUNT(*) AS tickets_bought
FROM lottery_tickets GROUP BY lid ) b ON a.id = b.lid
WHERE a.cid = 1
ORDER BY started DESC
LIMIT 1;
This is the query I'm performing (without some Joins that are not relevant):
SELECT a.*, c.id
FROM a
LEFT OUTER JOIN b ON a.id = b.id_anunciante
LEFT OUTER JOIN c ON c.id = b.id_rubro
GROUP BY a.id
Each row of "a" is linked with 1 to 5 rows in "b".
The problem is that GROUP BY has performance issues (it takes 10x or more using GROUP BY than not using it). I need to retrieve only one row of each member in "a".
How can I make this faster?
edit: I need to be able to filter by a.id AND/OR c.id. The resultset I should be getting is only 1 row per "valid" member of "a", meaning the rows that match the constraints. Rows that don't match the filters shouldn't be returned.
In my original query, this would be done this way:
SELECT a.*, c.id
FROM a
LEFT OUTER JOIN b ON a.id = b.id_anunciante
LEFT OUTER JOIN c ON c.id = b.id_rubro
WHERE c.id = 1
OR a.id = 1
GROUP BY a.id
a.id, b.id_anunciante, b.id_rubro, c.id are all indexes.
SELECT a.*,
(
SELECT c.id
FROM b
JOIN с
ON c.id = b.id_rubro
WHERE b.id_anunciante = a.id
-- add the ORDER BY condition to define which row will be selected.
LIMIT 1
)
FROM a
Create the index on b (id_anunciante) for this to work faster.
Update:
You don't need the OUTER JOINs here.
Rewrite your query as this:
SELECT a.*, c.id
FROM a
JOIN b
ON b.id_anunciante = a.id
JOIN c
ON c.id = b.id_rubro
WHERE a.id = 1
UNION ALL
SELECT a.*, 1
FROM a
WHERE EXISTS
(
SELECT NULL
FROM c
JOIN b
ON b.id_rubro = c.id
WHERE c.id = 1
AND b.id_anunciante = a.id
)
Add ORDER BY NULL to avoid the implicit sorting MySQL does when doing a group by.
I suppose you have indexes/PKs on a.id, b.id_anunciante, b.id_rubro and c.id ? I guess you could try adding a composite index on (b.id_anunciante, b.id_rubro) if your mysql version is not able to do an index merge.