MYSQL Optimize query - mysql

I am trying to optimize a query in Mysql, now this query needs 0,5 seconds and the table only have 2500 rows.
I have 2 tables one table is the tickets and the other the tickets that are grouped.
Tickets:
- ID : Int (Primary Key)
- Name of the ticket: text
GroupTickets:
- ID : Int (Primary Key)
- ID_relation: Int -> the id of the group of the tickets
- ID_Ticket: Int -> the id of the ticket
My query is:
Select T.id, T.name, Count(GP.id_relation)
FROM Tickets as T, GroupTickets as GP
WHERE GP.id_relation IN (
Select id_relation from GroupTickets
Where id_ticket=T.id
)
This select in the where clause make that mysql will do a select by every row so in the future where the table have millions of rows this query will be hard to process.
Am i wrong? Someone know a better way to take this info? I need to know if a ticket is grouped with other tickets in the query.
Best Regards.

Try this (Your query has a redundant IN SELECT ...)
Select T.id, T.name, Count(GP.id) AS RC
FROM Tickets as T
INNER JOIN GroupTickets as GP ON GP.ID_TICKET = T.ID
GROUP BY T.ID, T.name
New version for new question as in comment below:
May be you want something like this:
SELECT A.ID, A.NAME, B.ID_RELATION, C.RC
FROM TICKETS A
INNER JOIN GROUPTICKETS B ON B.ID_TICKET = A.ID
INNER JOIN (SELECT ID_RELATION, COUNT(*) AS RC
FROM GROUPTICKETS A
GROUP BY ID_RELATION) C ON C.ID_RELATION = B.ID_RELATION
;
Could also be useful
CREATE INDEX GROUPTICKETS_IX01 ON GROUPTICKETS(ID_RELATION, ID_TICKET);

Related

Creating a SQL view with personal best records

I have the following SQL Database structure:
Users are the registered users. Maps are like circuits or race tracks. When a user is driving a time a new time record will be created including the userId, mapId and the time needed to finish the racetrack.
I wish to create a view where all the users personal bests on all maps are listed.
I tried creating the view like this:
CREATE VIEW map_pb AS
SELECT MID, UID, TID
FROM times
WHERE score IN (SELECT MIN(score) FROM times)
ORDER BY registered
This does not lead to the wished result.
Thank you for your help!
I hope that you have 'times' table created as the above diagram and 'score' column in the table that you use to measure the best record.
(MIN(score) is the best record).
You can simply create a view to have the personal best records using sub-queries like this.
CREATE VIEW map_pb AS
SELECT a.MID, a.UID, a.TID
FROM times a
INNER JOIN (
SELECT TID, UID, MIN(score) score
FROM times
GROUP BY UID
) b ON a.UID = b.UID AND a.score= b.score
-- if you have 'registered' column in the 'times' table to order the result
ORDER BY registered
I hope this may work.
You probably need to use a query that will first return the minimum score for each user on each map. Something like this:
SELECT UID,
MID,
MIN(score) AS best_time
FROM times
GROUP BY UID, MID
Note: I used MIN(score) as this is what is shown in your example query, but perhaps it should be MIN(time) instead?
Then just use the subquery JOINed to your other tables to get the output:
SELECT *
FROM (
SELECT UID,
MID,
MIN(score) AS best_time
FROM times
GROUP BY UID, MID
) a
INNER JOIN users u ON u.UID = a.UID
INNER JOIN maps m ON m.MID = a.MID
Of course, replace SELECT * with the columns you actually want.
Note: code untested but does give an idea as to a solution.
Start with a subquery to determine each user's minimum score on each map
SELECT UID, TID, MIN(time) time
FROM times
GROUP BY UID, TID
Then join that subquery into a main query.
SELECT times.UID, times.TID,
mintimes.time
FROM times
JOIN (
) mintimes ON times.TID = mintimes.TID
AND times.UID = mintimes.UID
AND times.time = mintimes.time
JOIN maps ON times.MID = maps.MID
JOIN users ON times.UID = users.UID
This query pattern uses a GROUP BY function to find the outlying (MIN in this case) value for each combination. It then uses that subquery to find the detail record for each outlying value.

SQL Query for getting maximum value from a column Joining from Another Table

This is a slight variant of the question I asked here
SQL Query for getting maximum value from a column
I have a Person Table and an Activity Table with the following data
-- PERSON-----
------ACTIVITY------------
I have got this data in the database about users spending time on a particular activity.
I intend to get the data when every user has spent the maximum number of hours.
My Query is
Select p.Id as 'PersonId',
p.Name as 'Name',
act.HoursSpent as 'Hours Spent',
act.Date as 'Date'
From Person p
Left JOIN (Select MAX(HoursSpent), Date from Activity
Group By HoursSpent, Date) act
on act.personId = p.Id
but it is giving me all the rows for Person and not with the Maximum Numbers of Hours Spent.
This should be my result.
You have several issues with your query:
The subquery to get hours is aggregated by date, not person.
You don't have a way to bring in other columns from activity.
You can take this approach -- joins and group by, but it requires two joins:
select p.*, a.* -- the columns you want
from Person p left join
activity a
on a.personId = p.id left join
(select personid, max(HoursSpent) as max_hoursspent
from activity a
group by personid
) ma
on ma.personId = a.personId and
ma.max_hoursspent = a.hoursspent;
Note that this can return duplicates for a given person -- if there are ties for the maximum.
This is written more colloquially using row_number():
select p.*, a.* -- the columns you want
from Person p left join
(select a.*,
row_number() over (partition by a.personid order by a.hoursspent desc) as seqnum
from activity a
) a
on a.personId = p.id and a.seqnum = 1
ma.max_hoursspent = a.hoursspent;

SQL query long execute (count in nested select)

I have such a query:
SELECT DISTINCT type, (SELECT count(*) FROM ads WHERE ad_type = description_table.type)
as count FROM description_table;
It takes for about 5 minutes to execute. What can be the problem here?
EDIT: Changed the table name from 'desc' to 'description_table' to avoid complication.
You need to join your table description_table with ads table. Try this:
SELECT DISTINCT type, (SELECT count(ads.type) FROM ads join description_table on ads.type = description_table.type)
as count FROM `description_table`;
and instead of counting *, try to count some column like id or type
EDIT:
As per your comment you can try this query:
SELECT a.type, count(d.type) as count
FROM description_table d left join ads a on d.type = a.type
group by d.type;

Slow aggregate query with join on same table

I have a query to show customers and the total dollar value of all their orders. The query takes about 100 seconds to execute.
I'm querying on an ExpressionEngine CMS database. ExpressionEngine uses one table exp_channel_data, for all content. Therefore, I have to join on that table for both customer and order data. I have about 14,000 customers, 30,000 orders and 160,000 total records in that table.
Can I change this query to speed it up?
SELECT link.author_id AS customer_id,
customers.field_id_122 AS company,
Sum(orders.field_id_22) AS total_orders
FROM exp_channel_data customers
JOIN exp_channel_titles link
ON link.author_id = customers.field_id_117
AND customers.channel_id = 7
JOIN exp_channel_data orders
ON orders.entry_id = link.entry_id
AND orders.channel_id = 3
GROUP BY customer_id
Thanks, and please let me know if I should include other information.
UPDATE SOLUTION
My apologies. I noticed that entry_id for the exp_channel_data table customers corresponds to author_id for the exp_channel_titles table. So I don't have to use field_id_117 in the join. field_id_117 duplicates entry_id, but in a TEXT field. JOINING on that text field slowed things down. The query is now 3 seconds
However, the inner join solution posted by #DRapp is 1.5 seconds. Here is his sql with a minor edit:
SELECT
PQ.author_id CustomerID,
c.field_id_122 CompanyName,
PQ.totalOrders
FROM
( SELECT
t.author_id
SUM( o.field_id_22 ) as totalOrders
FROM
exp_channel_data o
JOIN
exp_channel_titles t ON t.author_id = o.entry_id AND o.channel_id = 3
GROUP BY
t.author_id ) PQ
JOIN
exp_channel_data c ON PQ.author_id = c.entry_id AND c.channel_id = 7
ORDER BY CustomerID
If this is the same table, then the same columns across the board for all alias instances.
I would ensure an index on (channel_id, entry_id, field_id_117 ) if possible. Another index on (author_id) for the prequery of order totals
Then, start first with what will become an inner query doing nothing but a per customer sum of order amounts.. Since the join is the "author_id" as the customer ID, just query/sum that first. Not completely understanding the (what I would consider) poor design of the structure, knowing what the "Channel_ID" really indicates, you don't want to duplicate summation values because of these other things in the mix.
select
o.author_id,
sum( o.field_id_22 ) as totalOrders
FROM
exp_channel_data customers o
where
o.channel_id = 3
group by
o.author_id
If that is correct on the per customer (via author_id column), then that can be wrapped as follows
select
PQ.author_id CustomerID,
c.field_id_122 CompanyName,
PQ.totalOrders
from
( select
o.author_id,
sum( o.field_id_22 ) as totalOrders
FROM
exp_channel_data customers o
where
o.channel_id = 3
group by
o.author_id ) PQ
JOIN exp_channel_data c
on PQ.author_id = c.field_id_117
AND c.channel_id = 7
Can you post the results of an EXPLAIN query?
I'm guessing that your tables are not indexed well for this operation. All of the columns that you join on should probably be indexed. As a first guess I'd look at indexing exp_channel_data.field_id_117
Try something like this. Possibly you have error in joins. also check whether joins on columns are correct in your databases. Cross join may takes time to fetch large data, by mistake if your joins are not proper on columns.
select
link.author_id as customer_id,
customers.field_id_122 as company,
sum(orders.field_id_22) as total_or_orders
from exp_channel_data customers
join exp_channel_titles link on (link.author_id = customers.field_id_117 and
link.author_id = customer.channel_id = 7)
join exp_channel_data orders on (orders.entry_id = link.entry_id and orders.entry_id = orders.channel_id = 3)
group by customer_id

complex(?) mysql correlated counting query. please help?

Suppose the following situation.
Persons assigned to tasks, and I want to return Person id, Person Name, the number of tasks completed by each person from the following tables.
Table Name - Field Name
Person - id, Name
Task_Person_Combi - Task_id, Person_id
Task* - returns id of Task (actually this is LEFT Joined table which returns id of persons)
(Task has over 100,000 rows, and the query has to be quick well less than 1 second)
After reading MySQL statement combining a join and a count?, I'm trying the following. (but this doesn't seem to work, and I'm kind of lost)
SELECT id, Name,
(
SELECT COUNT(*)
FROM Task_Person_Combi C
WHERE P.id=C.Person_id AND C.Task IN (SELECT id FROM Task* - this is Joined table)
) AS Count
FROM Person P
WHERE id>0
HAVING Count>0
ORDER BY Name
Please help.
Try this?
SELECT id, Name,
COUNT(T.ID) AS TaskCount
FROM Person AS P
INNER JOIN Task_Person_Combi AS C ON P.id=C.Person_id
LEFT JOIN TASK AS T ON C.Task = T.id
WHERE id>0
AND T.id IS NOT NULL
GROUP BY id,Name
HAVING COUNT(T.ID)>0
ORDER BY Name