MySQL - expensive subquery - mysql

I have this query that was taking 13 seconds to run, consistently:
SELECT COUNT(*) AS Counter
FROM Groups_Users gu
JOIN Groups g ON gu.GroupID = g.GroupID
WHERE (
(gu.UserID = 3 AND gu.IsAdmin = 1 AND g.GroupID = 395)
OR g.OrganizationID IN (
SELECT OrganizationID FROM Organizations_Users WHERE UserID = 3 AND IsAdmin = 1 )
)
After adding indexes to Groups_Users and Organization_Users, it went to less than 1 second. So, problem solved, except that I expect the number of records in these 2 tables to grow a lot. And I'm wondering if my subselect and/or join is inefficient and could be made better. Maybe at some point the performance will degrade again when the record count goes from 8,000 to 100,000 because the subselect is inefficient.
Any ideas on making this query better?

SELECT COUNT(*) AS Counter
FROM Groups_Users gu
JOIN Groups g ON gu.GroupID = g.GroupID
WHERE
(
( gu.UserID = 3
AND gu.IsAdmin = 1
AND g.GroupID = 395
)
OR EXISTS
( SELECT 1
FROM Organizations_Users ou
WHERE ou.UserID = 3
AND ou.IsAdmin = 1
AND ou.organizationid = g.organizationid
)
)
basically you say select 1 where these conditions are met.. if they are true then the exists query is true and it will include it. so all you have to do is add AND ou.organizationid = g.organizationid to the subquery and you're good to go

Related

Improving a SQL query when I need to select IN multiple fields combined together

Here is my SQL query
SELECT date(metrics_session.created_at) as day, COUNT(metrics_session.user_id) as total_logins,
sum(TIMESTAMPDIFF(MINUTE,metrics_session.created_at,metrics_session.completed_at)) as total_time_spent
FROM metrics_session
inner join metrics_training on metrics_training.id = metrics_session.training_id
inner join metrics_course on metrics_course.id = metrics_training.course_id
inner join metrics_user_training_cohort on metrics_training.id = metrics_user_training_cohort.training_id
inner join auth_user on auth_user.id = metrics_user_training_cohort.user_id
WHERE metrics_session.created_at >= '2021-01-15'
AND metrics_session.created_at <= '2022-10-15'
AND metrics_session.completed_at IS NOT NULL
AND metrics_session.user_id In (SELECT user_id from metrics_user_training_cohort where user_id = 44 and training_id = 4)
AND metrics_session.training_id In (SELECT training_id from metrics_user_training_cohort where user_id = 44 and training_id = 4)
#AND EXISTS(SELECT user_id,training_id from metrics_user_training_cohort where user_id = 44 and training_id = 4)
GROUP BY date(metrics_session.created_at) ORDER BY date(metrics_session.created_at)
the goal of this query is to select the sessions that were created by some user_id and are linked to some training_id , but only if in the table metrics_user_training_cohort I have that same user_id and training_id in the same row registered.
I managed to achieve that with the 2 last lines before GROUP BY:
AND metrics_session.user_id In (SELECT user_id from metrics_user_training_cohort where user_id = 44 and training_id = 4)
AND metrics_session.training_id In (SELECT training_id from metrics_user_training_cohort where user_id = 44 and training_id = 4)
however the repeated subquery used for the IN statement seems unnecessary to me and likely degrading performance, but I can't quite figure out a better way since the IN statement can only be used with 1 column.
The commented line is not the solution because it just checks for the existence of the row in the table in isolation without association to the sessions, but I left it there to give you a better idea what I'm trying to achieve.
It is usually better to use JOIN or EXISTS instead of IN ( SELECT ... )
These may help:
metrics_session: INDEX(created_at, completed_at, user_id, training_id)
metrics_user_training_cohort: INDEX(training_id, user_id)

how to optimize mysql query with inner join

select id from customer_details where store_client_id = 2
And
id NOT IN (select customer_detail_id from orders
where store_client_id = 2 and total_spent > 100 GROUP BY customer_detail_id )
Or
id IN (select tcd.id from property_details as pd, customer_details as tcd
where pd.store_client_id = 2 and pd.customer_detail_id = tcd.customer_id and pd.property_key = 'Accepts Marketing'
and pd.property_value = 'no')
And
id IN (select customer_detail_id from orders
where store_client_id = 2 GROUP BY customer_detail_id HAVING count(customer_detail_id) > 0 )
Or
id IN (select tor.customer_detail_id from ordered_products as top, orders as tor
where tor.id = top.order_id and tor.store_client_id = 2
GROUP BY tor.customer_detail_id having sum(top.price) = 1)`
I have this mysql query with inner join so when it run in mysql server it slow down what is the issue cant find.
But after 4-5 minutes it return 15 000 records. This records is not an issue may be.
In some tutorial suggest to use Inner join, Left join,...
But I don't know how to convert this query in Join clause.
Any help will be appreciated. Thanks in advance.
First of all please read relational model and optimizing select statements.

updating in a query with joins. Counter to be increased by number of rows

I have written a following query.
UPDATE
tbl_bookings tb
INNER JOIN
tbl_slots ts
ON ( tb.slot_id = ts.id )
SET tb.seat_freed = 1, ts.free_machines = ts.free_machines + 1
WHERE 1
AND tb.seat_freed = 0
AND tb.transactionComplete = 0
Here I am trying to free the seats by updating the seat_freed to 1 and increasing the free_machines counter by 1.
In case, there are more than 1 rows (say 3 rows) returned from tbl_bookings, I would want to increment the counter by .
Is there any way to do it, using the single. I can obviously do it by breaking it down into different queries, but single query is what I desire. :)
You could use a subquery with the exact same conditions to calculat the number of rows which will be affected by the update. I used DISTINCT for the count since i don't know how bookings and slots are related in your example.
UPDATE tbl_bookings tb
INNER JOIN tbl_slots ts ON ( tb.slot_id = ts.id )
INNER JOIN (SELECT count(DISTINCT b.id) seats_to_be_freed
FROM tbl_bookings b INNER JOIN tbl_slots s ON ( b.slot_id = s.id )
WHERE b.seat_freed=0 and b.transactionComplete=0) tmp
SET tb.seat_freed = 1, ts.free_machines = tmp.seats_to_be_freed
WHERE 1
AND tb.seat_freed = 0
AND tb.transactionComplete = 0

MySQL select in update statement

This MySQL statement give me all id_duel_player for player with id_player=30 and it work fine.
SELECT b.id_duel_player
FROM duels a
INNER JOIN duel_player b
ON a.id_duel = b.id_duel
WHERE id_player = 30
UNION ALL
SELECT c.id_duel_player
FROM duel_player c
INNER JOIN
(
SELECT aa.*
FROM duels aa
INNER JOIN duel_player bb
ON aa.id_duel = bb.id_duel
WHERE bb.id_player = 30
) d ON c.id_duel = d.id_duel AND c.id_player <> 30
I want to make MySQL statement for UPDATE (fields from duel_player table) all of this id_duel_player that returns this select statement.
UPDATE duel_player
SET num = 2,
total = 5
WHERE (duel_player.id_duel_player = id_duel_player's from above SELECT statement)
I want most effective and fastest way to do this.
Thanks
For 200-400 rows it's likely fastest to create a temporary table with the results, and then do the UPDATE with a join:
CREATE TEMPORARY TABLE id_duel_players AS
SELECT b.id_duel_player as id FROM duels a ...
UPDATE duel_player
JOIN id_duel_players ON duel_player.id_duel_player = id_duel_players.id
SET num = 2,
total = 5
For smaller result sets you may find the IN operator sufficiently fast (... WHERE id_duel_player IN (SELECT ...)), but I've found it unreliable for result sets with hundreds of rows. (Unreliable = suddenly no matches are found, no idea why, I haven't investigated.)

Mysql query optimization issue

I am facing an query optimizing problem. Hope I will get some help.
The scenario is I have 4 tables.
Table 1 [asset_id, asset_name, User_id].
Table 2 [company_id, Company_name]
Table 3 [User_id, User_name]
Table 4 [Map_id, User_id, Company_id ]
My result will be to check Users from same company cannot able to upload same asset, checking of asset will be on COmpany level.
I have already written a query, that serves the purpose as well, but i need to eliminate the sub query for optimizing purpose.
The query I have written has a sub query.
My query is
SELECT COUNT(tg.asset_id)
FROM Table 1 tg
INNER JOIN Table 4 mcu
ON ((tg.User_id = mcu.User_id )
AND mcu.Company_id = (select Table 4.Company_id
from Table 4
where Table 4.User_id = 1))
WHERE tg.asset_name = 't1' ;
Hope this may be helpful::
SELECT
COUNT(tg.asset_id)
FROM Table 1 tg
INNER JOIN Table 4 mcu ON (tg.User_id = mcu.User_id and Company_id = ClientID )
WHERE tg.asset_name = 't1' and mcu.User_id = 1
The subquery seems unneeded for this query since it also uses Table 4.
SELECT COUNT(tg.asset_id) FROM Table 1 tg
INNER JOIN Table 4 mcu ON tg.User_id = mcu.User_id
WHERE tg.asset_name = 't1'
AND mcu.ClientID = mcu.Company_id
AND mcu.User_id = 1;