MySQL query too slow about 25 seconds - mysql

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).

Related

mysql: simpler and more efficient query for getting unique products

I would like to optimize my database query but I am not sure how to do this.
I want to get a list of stores' products opinions, ordered by opinion dates (from newest to oldest ones), but the products need to be unique.
For example, there are 3 users: U1, U2, U3.
There are 2 stores in the city:
S1 (with products P11, P12, P13, P14)
S2 (with products P21, P22, P23, P24)
Users added some opinions (the newest on the top, the oldest on the bottom):
U1: P22
U1: P13
U2: P21
U3: P13
U2: P23
U1: P23
What I want to achieve is:
U1: P22
U1: P13
U2: P21
U2: P23
The query I created is very long and a bit complicated. Could I simplify it somehow?
$sql_query = "
SELECT a.*
, b.name AS 'store_name'
, b.city AS 'store_city'
, c.name AS 'product_name'
FROM `app_products_opinion` AS a
JOIN `app_products_stores` AS b
ON a.store_ID = b.ID
JOIN `app_products` AS c
ON a.product_ID = c.ID
WHERE a.created_on IN
(
SELECT max(created_on) as created_on
FROM app_products_opinion
WHERE show_on_list='1' AND (added_by='".$_SESSION["CMSUserID"]."' OR status = '1')
GROUP by product_ID
ORDER by created_on DESC
)
AND a.show_on_list='1'
AND a.store_ID='".$id_store['ID']."' $addtosql
AND a.photo != ''
AND (a.added_by='".$_SESSION["CMSUserID"]."' OR a.status='1')
ORDER BY a.created_on DESC
";
You could try grouping by product_id and also joining by product_ID and date
(simplified code)
SELECT a.user_id, a.product_ID
from app_products_opinion a
INNER JOIN (
SELECT product_ID, max(created_on) as created_on
FROM app_products_opinion
WHERE show_on_list='1' AND (added_by='".$_SESSION["CMSUserID"]."' OR status = '1')
GROUP by product_ID
ORDER by created_on DESC
) t on a.created_on = t.created_on
AND a.product_ID = t.product_ID
I don't know if you think it's simpler (and ignoring, $addtosql) but you could do this...
SELECT a.*
, b.name AS 'store_name'
, b.city AS 'store_city'
, c.name AS 'product_name'
FROM `app_products_opinion` AS a
JOIN `app_products_stores` AS b
ON a.store_ID = b.ID
JOIN `app_products` AS c
ON a.product_ID = c.ID
JOIN
(
SELECT product_id
, max(created_on) created_on
FROM app_products_opinion
WHERE show_on_list = 1
AND (added_by = 'M' OR status = 1)
GROUP
by product_ID
) x
ON a.created_on = x.created_on
AND a.product_id = x.product_id
AND a.show_on_list = 1
AND a.store_ID = 'N'
AND a.photo != ''
AND (a.added_by = 'Z' OR a.status = 1)

Optimizing MySQL query with multiple joins and Sub query

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

How to optimize for speed a sql multiple select with SUM

I have a really long select from my database with many joins. The problem is with counting SUM: without sum, select time is about 3s, but with SUM is about 15s.
Is it possible to optimize my select to obtain a shorter select time?
Here is my code:
SELECT
accomodation.id,
accomodation.aid,
accomodation.title_en,
accomodation.title_url_en,
accomodation.address,
accomodation.zip,
accomodation.stars,
accomodation.picture,
accomodation.valid_from,
accomodation.valid_to,
accomodation.latitude,
accomodation.longitude,
accomodation.city_id AS
accomodation_city_id,
db_cities.id AS city_id,
db_cities.title_en AS city,
db_cities.title_url AS city_url,
db_countries.title_en AS country_title,
db_countries.title_url_en AS country_url,
accomodation_type.class AS accomodation_type_class,
accomodation_review_value_total.value AS review_total,
MIN(accomodation_price.price) AS price_from,
accomodation_rooms.total_persons
FROM
(SELECT aid, MAX(info_date_add) AS max_info_date_add FROM accomodation GROUP BY aid) accomodation_max
INNER JOIN accomodation
ON
accomodation_max.aid = accomodation.aid AND
accomodation_max.max_info_date_add = accomodation.info_date_add
LEFT JOIN db_cities
ON (
db_cities.id = accomodation.city_id OR
(((acos(sin((db_cities.latitude*pi()/180)) * sin((accomodation.latitude*pi()/180)) + cos((db_cities.latitude*pi()/180)) * cos((accomodation.latitude*pi()/180)) * cos(((db_cities.longitude - accomodation.longitude)*pi()/180))))*180/pi())*60*1.1515*1.609344) < '20')
JOIN db_countries
ON db_countries.id = accomodation.country_id
LEFT JOIN accomodation_review_value_total
ON accomodation_review_value_total.accomodation_aid = accomodation.aid
LEFT JOIN accomodation_type_value
ON accomodation_type_value.accomodation_id = accomodation.id
LEFT JOIN accomodation_type
ON accomodation_type.id = accomodation_type_value.accomodation_type_id
JOIN accomodation_season
ON (
accomodation_season.accomodation_aid = accomodation.aid AND
( '2013-11-04' BETWEEN accomodation_season.start_date AND accomodation_season.end_date OR '2013-11-05' BETWEEN accomodation_season.start_date AND accomodation_season.end_date ) )
JOIN accomodation_price
ON
accomodation_price.accomodation_aid = accomodation.aid AND
accomodation_price.accomodation_price_type_id = '1' AND
accomodation_price.accomodation_price_cat_id = '1' AND
accomodation_price.price BETWEEN '20' AND '250' AND
accomodation_price.accomodation_season_id = accomodation_season.id
JOIN accomodation_theme_value
ON accomodation_theme_value.accomodation_id = accomodation.id
INNER JOIN
(SELECT
accomodation_id,
SUM(accomodation_rooms.rooms) AS total_rooms,
SUM(accomodation_rooms.beds * accomodation_rooms.rooms) AS total_persons
FROM accomodation_rooms
GROUP BY accomodation_id) accomodation_rooms
ON
accomodation_rooms.accomodation_id = accomodation.id AND
accomodation_rooms.total_persons >= '4'
WHERE
db_countries.title_url_en LIKE '%spain%' AND
db_cities.title_url LIKE '%barcelona%' AND
accomodation_type_value.accomodation_type_id IN (5,10) AND
total_rooms >= '2' AND
accomodation_theme_value.accomodation_theme_id IN (11,12,13) AND
accomodation.stars IN (3,4,5) AND
( accomodation_review_value_total.value >= '4.5' ) AND
db_cities.id = '2416'
GROUP BY accomodation.aid
ORDER BY
CASE
WHEN accomodation.valid_to>=NOW() AND accomodation.valid_from<=NOW() AND MIN(accomodation_price.price) IS NOT NULL THEN 0
WHEN NOW()>accomodation.valid_to AND accomodation.valid_to>'0000-00-00' AND MIN(accomodation_price.price) IS NOT NULL THEN 1
WHEN accomodation.valid_to>=NOW() AND accomodation.valid_from<=NOW() THEN 2
WHEN NOW()>accomodation.valid_to AND accomodation.valid_to>'0000-00-00' THEN 3
ELSE 4 END,
review_total DESC,
accomodation.title_en
LIMIT 10

Delete from multiple tables using order by and limit

I'm trying to delete from two tables which have a relationship using ORDER BY DESC and LIMIT.
DELETE FROM my_rel_table AS t1
LEFT JOIN my_photo_table AS t2 ON t2.typeid = t1.typeid
WHERE t1.relid = 1
AND t1.type = 1
ORDER BY t1.id DESC
LIMIT 1
Obviously the above does not work since mysql does not accept the order by and limit using an inner join.
Table structure is as follows:
my_rel_table
id relid relno typeid type
int int int int tinyint
my_photo_table
typeid pos_x pos_y width height
int int int int int
Doing it using a JOIN against a subselect to get the highest id from the my_rel_table
DELETE my_rel_table, my_photo_table
FROM my_rel_table
INNER JOIN
(
SELECT MAX(id) AS MaxId
FROM my_rel_table
WHERE relid = 1
AND type = 1
) Sub1
ON my_rel_table.id = Sub1.MaxId
LEFT OUTER JOIN my_photo_table ON my_photo_table.typeid = my_rel_table.typeid
WHERE my_rel_table.relid = 1
AND my_rel_table.type = 1
Not directly tested as I have no test data!
EDIT - Couple of attempts to do the top 5, but again not tested
DELETE my_rel_table, my_photo_table
FROM my_rel_table
INNER JOIN
(
SELECT id
FROM my_rel_table
WHERE relid = 1
AND type = 1
ORDER BY id DESC
LIMIT 5
) Sub1
ON my_rel_table.id = Sub1.id
LEFT OUTER JOIN my_photo_table ON my_photo_table.typeid = my_rel_table.typeid
WHERE my_rel_table.relid = 1
AND my_rel_table.type = 1
Or a different way.
DELETE my_rel_table, my_photo_table
FROM my_rel_table
INNER JOIN
(
SELECT id, #Counter:=#Counter+1 AS ItemCounter
FROM my_rel_table
CROSS JOIN (SELECT #Counter:=0) Sub1
WHERE relid = 1
AND type = 1
ORDER BY id DESC
) Sub1
ON my_rel_table.id = Sub1.id
AND Sub1.ItemCounter <= 5
LEFT OUTER JOIN my_photo_table ON my_photo_table.typeid = my_rel_table.typeid
WHERE my_rel_table.relid = 1
AND my_rel_table.type = 1

MySQL Query Efficiency - Is there a better way?

I have a query that basically combines tables of actions and selects from them in chronological order while preserving pagination..
Is there a more efficient / better way to do this? The query takes 3 seconds. Not terrible.. but I think there is room for improvement and I will be using it alot..
Thanks!
SELECT
`newsletters_subscribers`.`email`,
`newsletters_subscribers`.`first_name`,
`newsletters_subscribers`.`last_name`,
`newsletters_subscribers`.`id` AS subscriber_id,
COUNT(DISTINCT newsletters_opens.id) AS opens,
COUNT(DISTINCT newsletters_clicks.id) AS clicks,
COUNT(DISTINCT newsletters_forwards.id) AS forwards
FROM `thebookrackqccom_newsletters_subscribers` newsletters_subscribers
LEFT JOIN
`thebookrackqccom_newsletters_opens` newsletters_opens
ON `newsletters_opens`.`subscriber_id` = `newsletters_subscribers`.`id`
AND newsletters_opens.newsletter_id = 1
LEFT JOIN
`thebookrackqccom_newsletters_clicks` newsletters_clicks
ON `newsletters_clicks`.`subscriber_id` = `newsletters_subscribers`.`id`
AND newsletters_clicks.newsletter_id = 1
LEFT JOIN
`thebookrackqccom_newsletters_forwards` newsletters_forwards
ON `newsletters_forwards`.`subscriber_id` = `newsletters_subscribers`.`id`
AND newsletters_forwards.newsletter_id = 1
WHERE
( newsletters_opens.id IS NOT NULL
OR newsletters_clicks.id IS NOT NULL
OR newsletters_forwards.id IS NOT NULL )
GROUP BY
`newsletters_subscribers`.`id`
ORDER BY
`newsletters_subscribers`.`email` ASC
LIMIT 25
What you need is indexes that the query can use. A compound index on (newsletter_id, subscribe_id) on each one of the three tables would help.
You can also rewrite the query like this:
SELECT
s.email,
s.first_name,
s.last_name,
s.id AS subscriber_id,
COALESCE(o.opens, 0) AS opens,
COALESCE(c.clicks, 0) AS clicks,
COALESCE(f.forwards, 0) AS forwards
FROM thebookrackqccom_newsletters_subscribers AS s
LEFT JOIN
( SELECT subscriber_id,
COUNT(*) AS opens
FROM thebookrackqccom_newsletters_opens
WHERE newsletters_opens.newsletter_id = 1
) AS o ON o.subscriber_id = s.id
LEFT JOIN
( SELECT subscriber_id,
COUNT(*) AS clicks
FROM thebookrackqccom_newsletters_clicks
WHERE newsletter_id = 1
) AS c ON c.subscriber_id = s.id
LEFT JOIN
( SELECT subscriber_id,
COUNT(*) AS forwards
FROM thebookrackqccom_newsletters_forwards
WHERE newsletter_id = 1
) AS f ON f.subscriber_id = s.id
WHERE ( o.subscriber_id IS NOT NULL
OR c.subscriber_id IS NOT NULL
OR f.subscriber_id IS NOT NULL )
ORDER BY
s.email ASC
LIMIT 25
Try this Query i hope you get a better execution time
QUERY
SELECT
`newsletters_subscribers`.`email`,
`newsletters_subscribers`.`first_name`,
`newsletters_subscribers`.`last_name`,
`newsletters_subscribers`.`id` AS subscriber_id,
#nopen := coalesce( N_OPEN.NOPENIDCOUNT, 000000 ) as opens,
#nclick := coalesce( N_CLICK.NCLICKIDCOUNT, 000000 ) as clicks,
#nfwd := coalesce( N_FWD.NFWDIDCOUNT, 000000 ) as forwards
FROM
(select #nopen := 0,#nclick := 0,#nfwd :=0) sqlvars,
`thebookrackqccom_newsletters_subscribers` AS newsletters_subscribers
LEFT JOIN (SELECT `newsletters_opens`.`subscriber_id`,
COUNT(newsletters_opens.id) AS NOPENIDCOUNT
FROM `thebookrackqccom_newsletters_opens` AS newsletters_opens
WHERE newsletters_opens.newsletter_id = 1) AS N_OPEN
ON N_OPEN.subscriber_id = `newsletters_subscribers`.`id`
LEFT JOIN (SELECT `newsletters_clicks`.`subscriber_id`,
COUNT(newsletters_clicks.id) AS NCLICKIDCOUNT
FROM `thebookrackqccom_newsletters_clicks` AS newsletters_clicks
WHERE newsletters_clicks.newsletter_id = 1) AS N_CLICK
ON N_CLICK.subscriber_id = `newsletters_subscribers`.`id`
LEFT JOIN (SELECT `newsletters_forwards`.`subscriber_id`,
COUNT(newsletters_forwards.id) AS NFWDIDCOUNT
FROM `thebookrackqccom_newsletters_forwards` AS newsletters_forwards
WHERE newsletters_forwards.newsletter_id = 1) AS N_FWD
ON N_FWD.subscriber_id = `newsletters_subscribers`.`id`
GROUP BY `newsletters_subscribers`.`id`
ORDER BY `newsletters_subscribers`.`email` ASC
LIMIT 25