Optimizing MySQL query with multiple joins and Sub query - mysql

I am using the following query to get data from 10 table, It is working fine but quite slow, Is there any way to Optimizing the query.
Query: SELECT emi.emi_due_date,users.usr_mobile,users.usr_id,concat_ws(" ",users.usr_fname,users.usr_mname,users.usr_lname) as borrower,users.usr_status,users.usr_curnt_city, users.usr_email,emi.loan_id,emi.emi_show_date,sum(emi.emi_amount)-sum(ifnull(emi.settled_amount,0)) as due_amount,cb.cb_type,blr.bloan_collection_executive_id,blr.pp_allow,blr.bloan_legal_team_id,blr.bloan_legal_team_status,concat_ws(" ",cp.cp_fname,cp.cp_lname) as cp_name,cp.cp_mobile,cp.cp_firm_name,cp.cp_type,bg.guarantor_name,bg.guarantor_contact,pl.ecs_date,pd.p2p_date,
(SELECT instrument FROM borrower_payment_master WHERE loan_id = emi.loan_id order by id desc limit 0,1) as last_pmode,
(SELECT IFNULL(DATE_FORMAT(emi_show_date - INTERVAL 1 MONTH,"%m-%Y"),"") FROM emi AS e WHERE e.loan_id=emi.loan_id and e.emi_status < 2 ORDER by e.emi_show_date ASC limit 1) as paid_till,
(select payment_date from borrower_payment_master as bp where bp.loan_id=emi.loan_id order by bp.id desc limit 1) as last_emi_paid FROM emi AS emi
INNER JOIN borrower_loan_reg_requests AS blr ON emi.loan_id=blr.bloan_id
INNER JOIN users AS users ON users.usr_id=blr.bloan_user_id
INNER JOIN borrower_loan_disbursed_funds AS blf ON blf.df_bloan_id=emi.loan_id
LEFT JOIN channel_partners AS cp ON cp.cp_id=users.usr_cp_referral_id
LEFT JOIN borrower_posted_loans AS pl ON pl.pl_bloan_id=emi.loan_id
LEFT JOIN collection_bucket AS cb ON cb.cb_loan_id=emi.loan_id AND cb.cb_status = 1
LEFT JOIN borrower_guarantors AS bg ON bg.guarantor_borrower_id=users.usr_id
LEFT JOIN p2p_dates AS pd ON pd.p2p_loan_id=emi.loan_id AND pd.p2p_status = 1
WHERE emi.emi_status<2 AND emi.emi_amount != 0
AND (SELECT count(*) FROM borrower_payment_master as pm WHERE pm.loan_id = emi.loan_id
AND MONTH(pm.payment_date) = "'.date('m').'" AND YEAR(pm.payment_date) = "'.date('Y').'") = 0
AND (select s.settlement_date as sdate from settlement as s WHERE emi.loan_id=s.loan_id limit 1) !=""
group by emi.loan_id order by emi.loan_id desc

Related

MySQL join on substring is slow

I have a query where I do a join on a substring, the problem is that this is really slow to complete. Is there a more effecient way to write this?
SELECT *, SUM(s.pris*s.antall) AS total, SUM(s.antall) AS antall
FROM ecs_statistikk AS s
JOIN butikk_ordre AS bo ON ordreId=bo.ecs_ordre_id AND butikkNr=bo.site_id
JOIN ecs_supplier AS l ON SUBSTRING( s.artikkelId, 1,2 )=l.lev_id
WHERE s.salgsDato>='2016-6-01' AND s.salgsDato<='2016-09-30'
GROUP BY l.lev_id ORDER BY total DESC
First, I would check indexes. For this query:
SELECT *, SUM(s.pris*s.antall) AS total, SUM(s.antall) AS antall
FROM ecs_statistikk s JOIN
butikk_ordre bo
ON s.ordreId = bo.ecs_ordre_id AND
s.butikkNr = bo.site_id JOIN
ecs_supplier l
ON SUBSTRING(s.artikkelId, 1, 2 ) = l.lev_id
WHERE s.salgsDato >= '2016-06-01' AND s.salgsDato <= '2016-09-30'
GROUP BY l.lev_id
ORDER BY total DESC ;
You want indexes on ecs_statistikk(salgsDato, ordreId, butikkNr, artikkelId), butikk_ordre(ecs_ordre_id, site_id), and ecs_supplier(lev_id)`.
Next, I would question whether you need the last JOIN at all. Does this do what you want?
SELECT LEFT(s.artikkelId, 2) as lev_id, *,
SUM(s.pris*s.antall) AS total, SUM(s.antall) AS antall
FROM ecs_statistikk s JOIN
butikk_ordre bo
ON s.ordreId = bo.ecs_ordre_id AND
s.butikkNr = bo.site_id
WHERE s.salgsDato >= '2016-06-01' AND s.salgsDato <= '2016-09-30'
GROUP BY LEFT(s.artikkelId, 2)
ORDER BY total DESC ;

Optimize Query with JOINS and Subqueries

I want to speed up one of my slower queries.
The problem is that I can't access the outer colum value within a subquery.
What I have:
SELECT r.id AS room_id, r.room_name, coalesce(d.score,0) AS total_messages, d.latest
FROM cf_rooms_time_frames tf
INNER JOIN cf_rooms r on r.id = tf.room_id
INNER JOIN(
SELECT cf.room_id, count(*) as score, max(cf.id) as latest
FROM cf_rooms_messages cf
WHERE EXISTS(
SELECT NULL FROM cf_rooms_time_frames tf
WHERE tf.start <= cf.id AND ( tf.end IS NULL OR tf.end >= cf.id )
AND tf.room_id = cf.room_id AND tf.uid = 8
)
GROUP BY cf.room_id
ORDER BY latest
DESC ) d on d.room_id = r.id
WHERE tf.uid = 8
ORDER BY coalesce(latest, score) DESC LIMIT 0, 20
What I want:
SELECT r.id AS room_id, r.room_name, coalesce(d.score,0) AS total_messages, d.latest
FROM cf_rooms_time_frames tf
INNER JOIN cf_rooms r on r.id = tf.room_id
INNER JOIN(
SELECT cf.room_id, count(*) as score, max(cf.id) as latest
FROM cf_rooms_messages cf
/* line added here */
WHERE cf.room_id = tf.room_id
/* */
AND EXISTS(
SELECT NULL FROM cf_rooms_time_frames tf
WHERE tf.start <= cf.id AND ( tf.end IS NULL OR tf.end >= cf.id )
AND tf.room_id = cf.room_id AND tf.uid = 8
)
GROUP BY cf.room_id
ORDER BY latest
DESC ) d on d.room_id = r.id
WHERE tf.uid = 8
ORDER BY coalesce(latest, score) DESC LIMIT 0, 20
I think the markup explains what the query does.
It searches for "chatrooms" for a given user and orders them by the last message, gets the number of total message which ids are in a given range ( timeframes ), and the last message id.
I don't know why, but the first query returns all rows within the chatmessage table ( cf ) if I can trust EXPLAIN. It delivers the correct results but is kind of slow on a huge table.
I tested the second one with a "hardcoded" room_id and this one was very fast and doesn't "touched" the whole table.

2 requests in only one using mysql

I have two requests
UPDATE :
I need to do something like that :
SELECT poste_nom, ups_type_contrat,
(SELECT `entpro_date`
FROM ENT_PRO
WHERE entpro_user_id = 2
ORDER BY `entpro_id` DESC
LIMIT 1) ,
serv_nom,
serv_id_resp,
user_credit_cpf,
user_indice_salarial,
FLOOR( DATEDIFF( CURDATE( ) , user_dateentree ) /365 ) AS dateEntree
FROM USER
INNER JOIN USER_POSTE_SERVICE
ON USER.user_id= USER_POSTE_SERVICE.ups_poste_id
INNER JOIN POSTE
ON USER_POSTE_SERVICE. ups_poste_id = POSTE.poste_id
INNER JOIN SERVICE
ON USER_POSTE_SERVICE.ups_id_serv = SERVICE.serv_id
WHERE user_id = 2
ORDER BY user_nom ASC
Is it possible to gather two requests in only one ?
From what I understood you want to simple merge the result of your sub-query to your main SELECT, if so you could try it this way:
SELECT poste_nom,
ups_type_contrat,
ENT_PRO_RESULT.entpro_date,
serv_nom,
serv_id_resp,
user_credit_cpf,
user_indice_salarial,
FLOOR( DATEDIFF( CURDATE( ) , user_dateentree ) /365 ) AS dateEntree
FROM USER
LEFT JOIN (SELECT entpro_date,
entpro_user_id
FROM ENT_PRO
ORDER BY entpro_id DESC
LIMIT 1) ENT_PRO_RESULT
ON USER.user_id = ENT_PRO_RESULT.entpro_user_id
INNER JOIN USER_POSTE_SERVICE
ON USER.user_id = USER_POSTE_SERVICE.ups_poste_id
INNER JOIN POSTE
ON USER_POSTE_SERVICE.ups_poste_id = POSTE.poste_id
INNER JOIN SERVICE
ON USER_POSTE_SERVICE.ups_id_serv = SERVICE.serv_id
WHERE user_id = 2
ORDER BY user_nom ASC
I've joined it on:
ON USER.user_id = ENT_PRO_RESULT.entpro_user_id
So you only need to specify the:
WHERE user_id = 2
And the sub-query will use the current row user id for the LEFT JOIN.

MySQL query too slow about 25 seconds

I have a MySQL query and it takes about 25 sec. There are not many rows (just about 200) but I don't understand why it takes long time.
Query:
SELECT *
, c.id c_id
FROM campaign c
JOIN campaign_category cc
ON c.campaign_type = cc.id
WHERE c.is_deleted = 0
AND c.status = 1
AND c.id NOT IN (SELECT campaign_id FROM user_reviews WHERE user_id = 4)
AND c.amt_req > (SELECT COUNT(id)
FROM reserved_reviews
WHERE camping_id = c.id
AND user_id != 4)
+ (SELECT COUNT(id)
FROM user_reviews
WHERE campaign_id = c.id)
Edit:
I tried with JOIN like this but i got no result:
SELECT
*, `c`.`id` as `c_id`,COUNT(`ur`.`id`) as `total_reviewed`, COUNT(`rr`.`id`) as `total_reserved`
FROM
`campaign` `c`
JOIN `campaign_category` `cc` ON `c`.`campaign_type`=`cc`.`id`
JOIN `user_reviews` `ur` ON `ur`.`campaign_id`=`c`.`id`
JOIN `reserved_reviews` `rr` ON `rr`.`camping_id`=`c`.`id`
WHERE
`c`.`is_deleted` =0
AND
`c`.`status` = 1
AND
`ur`.`user_id` != 4
GROUP BY `c`.`id`
HAVING `c`.`amt_req` > COUNT(`ur`.`id`) + COUNT(`rr`.`id`)
Edit: Table structures: First Image - user_reviews Table, Second image campagin Table, Third image: reserved_reviews Table.
http://imgur.com/GI4817B,SdnSxuz,truxHM6#0
You can improve this query with indexes;
SELECT *, c.id c_id
FROM campaign c JOIN
campaign_category cc
ON c.campaign_type = cc.id
WHERE c.is_deleted = 0 AND
c.status = 1 AND
c.id NOT IN (SELECT campaign_id FROM user_reviews WHERE user_id = 4)
c.amt_req > (SELECT COUNT(*)
FROM reserved_reviews
WHERE campaign_id = c.id AND user_id <> 4)
) +
(SELECT COUNT(id)
FROM user_reviews
WHERE campaign_id = c.id
) ;
For the outer query and joins: campaign(status, is_deleted, id, amt_req) and campaign_category(id) (you should have the latter if it is defined as a primary key.
Then: user_reviews(user_id, campaign_id), reserved_reviews(campaign_id, user_id), and user_reviews(campaign_id).

MySQL use inner query in where clause

I have wrote the following SQL:
SELECT s.*, (SELECT COUNT(*) FROM orders o WHERE o.schedule_id = s.schedule_id AND o.content_id = s.content_id) as numofbookings
FROM schedules s
WHERE content_id = '{$contentid}'
AND DATEDIFF(`schedule_datetime_from`, now()) > 0
AND (SELECT COUNT(*) FROM orders o WHERE o.schedule_id = s.schedule_id AND o.content_id = s.content_id) < s.schedule_capacity
ORDER BY `s`.`schedule_datetime_from` ASC
Although I am concerned this could be wrote better without querying the orders table twice.
I tried AND numofbookings < s.schedule_capacity although this give error column not found.
SELECT s.*, COUNT(o.schedule_id) AS numofbookings
FROM schedules s
LEFT JOIN orders o
ON s.schedule_id = o.schedule_id
AND s.content_id = o.content_id
WHERE s.content_id = '{$contentid}'
AND DATEDIFF(s.schedule_datetime_from, now()) > 0
GROUP BY s.schedule_id, s.content_id
HAVING COUNT(o.schedule_id) < s.schedule_capacity
ORDER BY s.schedule_datetime_from ASC