Optimising the query which has joins and unions in mysql - mysql

I am using the laravel to build my web app. In that for listing all orders I have an query but it takes upto 40s in my localhost whereas in goddady shared hosting it takes upto 95s. Although I implemented the server side datatable yet the query takes bit long to execute. I need someone to suggest or help to make the query to excute more efficient. There are majorly two sets and I make that union.
select * from ((
select `subscription`.`id`,
`subscription`.`order_id`,
`users`.`name` as `user_name`,
`subscription`.`new_dated`,
`subscription`.`rescheduling_delivery_date` as expected_date,
COUNT(`subscription_items`.`product_id`) as items,
`subscription`.`od_dis_total` as `total`,
`subscription`.`order_discount`,
`area`.`area_name`,
`subscribe_orders`.`action` as ordertype,
driver.name as driver_name,
'NA' as payment_mode,
`payment_status`.`status_name` as payment_status,
`subscription_status`.`status_name` as order_status,
`subscription`.`od_payment_method`,
`subscription_items`.`product_id`,
subscription.delivered_dated
from `subscription`
left join `subscription_items` on `subscription`.`id` = `subscription_items`.`subscription_id`
left join `users` on `subscription`.`user_id` = `users`.`id`
left join `users` as `driver` on `subscription`.`driver_id` = `driver`.`id`
left join `ycias_address` on `subscription`.`add_id` = `ycias_address`.`id`
left join `area` on `ycias_address`.`area_id` = `area`.`id`
left join `subscribe_order_items` on `subscription`.`subscribe_order_id` = `subscribe_order_items`.`subscribe_order_id`
left join `subscribe_orders` on `subscription`.`subscribe_order_id` = `subscribe_orders`.`id`
left join `payment_status` on `subscription`.`payment_status` = `payment_status`.`id`
left join `subscription_status` on `subscription`.`od_status` = `subscription_status`.`id`
group by `subscription_items`.`subscription_id`)
union (
select `orders`.`id`,
`orders`.`order_id`,
`users`.`name` as `user_name`,
`orders`.`new_dated`,
`orders`.`expected_delivery_date` as expected_date,
COUNT(order_items.product_id) as items,
`orders`.`od_dis_total` as `total`,
`orders`.`order_discount`,
`area`.`area_name`,
'Instant Order' as ordertype,
driver.name as driver_name,
orders.payment_mode,
`payment_status`.`status_name` as payment_status,
`order_status`.`status_name` as order_status,
`orders`.`od_payment_method`,
`order_items`.`product_id`,
`orders`.`delivered_dated`
from `orders`
left join `order_items` on `orders`.`id` = `order_items`.`order_id`
left join `users` on `users`.`id` = `orders`.`user_id`
left join `users` as `driver` on `driver`.`id` = `orders`.`driver_id`
left join `ycias_address` on `orders`.`add_id` = `ycias_address`.`id`
left join `area` on `ycias_address`.`area_id` = `area`.`id`
left join `payment_status` on `orders`.`payment_status` = `payment_status`.`id`
left join `order_status` on `orders`.`od_status` = `order_status`.`id`
group by `order_items`.`order_id`)) as all_orders limit 10 offset 0

As per the suggestion given by Andy Lester I have added the index for the necessary columns. So the execution time is reduced to half that is 18 secs in localhost. I further investigated that the index columns are of datatype VARCHAR. So I changed that to INT and then executed, got results within 0.9 secs. Thanks to Andy Lester.

Related

Mysql update statement with join involved

I have 2 mysql tables called tbl_flats and tbl_blocks.
I'm joining these two tables like this:
select
`f`.`fld_id` AS `fld_flat_id`,
`b`.`fld_block_name`,
`f`.`fld_mobile_app_password`,
`f`.`fld_site_id`
from
`tbl_blocks` `b`
left join
`tbl_flats` `f` ON (`f`.`fld_block_id` = `b`.`fld_id`)
where
`f`.`fld_site_id` = 57
It displays like this:
Now what I'm trying to do is update fld_mobile_app_password columns to NULL. Here's what I've tried but failed:
update (select
`f`.`fld_id` AS `fld_flat_id`,
`b`.`fld_block_name`,
`f`.`fld_mobile_app_password`,
`f`.`fld_site_id`
from
`tbl_blocks` `b`
left join
`tbl_flats` `f` ON (`f`.`fld_block_id` = `b`.`fld_id`) )
set
`f`.`fld_mobile_app_password` = '1a1dc91c907325c69271ddf0c944bc72'
where
`f`.`fld_site_id` = 57
I can't seem to figure out the problem. Any tip is appriciated
You can't update columns in a subquery in MySQL. Just remove the subquery:
update `tbl_blocks` `b` join
`tbl_flats` `f`
on `f`.`fld_block_id` = `b`.`fld_id`
set `f`.`fld_mobile_app_password` = '1a1dc91c907325c69271ddf0c944bc72'
where `f`.`fld_site_id` = 57;
An inner join should be sufficient here -- the where clause turns the outer join into an inner join anyway.

MySQL Query, (access mainquery from subquery)

I have a problem that I need a WHERE clause in a subquery that depends on the results of the main Query, otherwise my results would be wrong and the query takes too long / is not executeable.
The circumstances that I need this query to create a view which I need for a search server support the problem that I cannot split this into two queries, nor process it with a script dynamically.
The problem occurs with the following query:
SELECT `s`.`id` AS `seminar_id`, (SUM( `sub`.`seminar_rate` ) / COUNT( `sub`.`seminar_id` )) AS `total_rate`
FROM
(
SELECT (SUM( value ) / COUNT( * )) AS `seminar_rate` , `r`.`seminar_id`
FROM `rating` r
INNER JOIN `rating_item` ri ON `r`.`id` = `ri`.`rating_id`
WHERE `r`.`seminar_id` = `s`.`id`/* <- Here is my problem, this is inacessible */
GROUP BY `r`.`seminar_id`
) AS sub,
`seminar` s
INNER JOIN `date` d
ON `s`.`id` = `d`.`seminar_id`
INNER JOIN `date_unit` du
ON `d`.`id` = `du`.`date_id`
LEFT JOIN `seminar_subject` su
ON `s`.`id` = `su`.`seminar_id`
LEFT JOIN `subject` suj
ON `su`.`subject_id` = `suj`.`id`
INNER JOIN `user` u
ON `s`.`user_id` = `u`.`id`
INNER JOIN `company` c
ON `u`.`company_id` = `c`.`id`
GROUP BY `du`.`date_id`, `sub`.`seminar_id`
This query should calculate a total rate out of ratings for each Seminar.
However my ratings are stored in my "rating" table and should be processed live.
(Sidenote: If you wonder about all the joins: This query has alooot more SELECT'ed fields, I just removed them because they are not nesessary to solve the problem and to make the query look less complicated [I know it still is >.>]...)
The reason is that I want this results to be sortable by my search engine later depending
on the users sort parameters, thatswhy I need it inside this query.
The problem itself is pretty obvious:
ERROR 1054 (42S22): Unknown column 's.id' in 'where clause'
The subselect doesnt know about the results of the main query, is there a solution to bypass this?
Could someone give me a hint to get this working?
Thanks in advance.
Using your subquery in the JOIN you can eliminate the WHERE clause and achieve nearly the same result. Here is your modified query. Hope this solves your problem.
SELECT `s`.`id` AS `seminar_id`, (SUM( `sub`.`seminar_rate` ) / COUNT( `sub`.`seminar_id` )) AS `total_rate`
FROM `seminar` s
INNER JOIN
(
SELECT (SUM( value ) / COUNT( * )) AS `seminar_rate` , `r`.`seminar_id`
FROM `rating` r
INNER JOIN `rating_item` ri ON `r`.`id` = `ri`.`rating_id`
/*WHERE `r`.`seminar_id` = `s`.`id` <- Here is my problem, this is inacessible */
GROUP BY `r`.`seminar_id`
) AS sub ON s.id = sub.`seminar_id`
INNER JOIN `date` d
ON `s`.`id` = `d`.`seminar_id`
INNER JOIN `date_unit` du
ON `d`.`id` = `du`.`date_id`
LEFT JOIN `seminar_subject` su
ON `s`.`id` = `su`.`seminar_id`
LEFT JOIN `subject` suj
ON `su`.`subject_id` = `suj`.`id`
INNER JOIN `user` u
ON `s`.`user_id` = `u`.`id`
INNER JOIN `company` c
ON `u`.`company_id` = `c`.`id`
GROUP BY `du`.`date_id`, `sub`.`seminar_id`

Mysql optimization based on explain

I have the following query that takes a really long time to execute. I need to speed it up, but I'm at a lost as to what technique to use. This is the query:
SELECT
`User`.`id`,
`User`.`username`,
`User`.`password`,
`User`.`role`,
`User`.`created`,
`User`.`modified`,
`User`.`email`,
`User`.`other_user_id`,
`User`.`first_name`,
`User`.`last_name`,
`User`.`place_id`,
`Resume`.`id`,
`Resume`.`user_id`,
`Resume`.`other_resume_id`,
`Resume`.`other_user_id`,
`Resume`.`file_extension`,
`Resume`.`created`,
`Resume`.`modified`,
`Resume`.`is_deleted`,
`Resume`.`has_file`,
`Resume`.`is_stamped`,
`Resume`.`is_active`
FROM
`streetofwalls`.`users` AS `User`
LEFT JOIN `my_database`.`attempts` AS `Attempt`
ON (`Attempt`.`user_id` = `User`.`id` AND `Attempt`.`test_id` != 5)
LEFT JOIN `my_database`.`reports` AS `Resume`
ON (`Resume`.`user_id` = `User`.`id`)
WHERE
`Attempt`.`test_id` = 8
AND `Attempt`.`score` > 60
AND `User`.`id` IN (
SELECT
`User1`.`id`
FROM
`my_database`.`users` AS User1
LEFT JOIN `my_database`.`tags_users` AS TagUser
ON (`User1`.`id`= `TagUser`.`user_id`)
LEFT JOIN `my_database`.`tags` AS Tag
ON (`TagUser`.`tag_id`= `Tag`.`id`)
WHERE `Tag`.`id` = (8) )
AND `User`.`id` NOT IN (
SELECT
`User1`.`id`
FROM
`my_database`.`users` AS User1
LEFT JOIN `my_database`.`tags_users` AS TagUser
ON (`User1`.`id`= `TagUser`.`user_id`)
LEFT JOIN `my_database`.`tags` AS Tag
ON (`TagUser`.`tag_id`= `Tag`.`id`)
WHERE `Tag`.`id` = (3) )
AND `Resume`.`has_file` = 1
GROUP BY `User`.`id`
ORDER BY `Attempt`.`score` DESC;
This query generates the following explain:
As you can see, I have several indexes on this query. At the moment only the resume table is not able to be indexed. Is is possible to index this table in the context of this query? Is there some other way to speed this query up that I have not thought of? Its prohibitively slow for its intended function and I'm out of ideas. Thank you to anyone who can help. Please let me know if any other information is needed.
try inner join instead of sub-query
it is default to guide query without running on data,but may be following the query will help you.
SELECT User.id, User.username, User.password, User.role, User.created, User.modified, User.email, User.other_user_id, User.first_name, User.last_name, User.place_id, Resume.id, Resume.user_id, Resume.other_resume_id, Resume.other_user_id, Resume.file_extension, Resume.created, Resume.modified, Resume.is_deleted, Resume.has_file, Resume.is_stamped, Resume.is_active
FROM
streetofwalls.users AS User
LEFT JOIN my_database.attempts AS Attempt ON (Attempt.user_id = User.id AND Attempt.test_id != 5)
LEFT JOIN my_database.reports AS Resume ON (Resume.user_id = User.id)
, my_database.users AS User1
LEFT JOIN my_database.tags_users AS TagUser on (User1.id= TagUser.user_id)
LEFT JOIN my_database.tags AS Tag ON (TagUser.tag_id= Tag.id)
WHERE
User.id = User1.id
AND Attempt.test_id = 8
AND Attempt.score > 60
AND Resume.has_file = 1
AND Tag.id = '8' AND Tag.id != '3'
GROUP BY User.id
ORDER BY Attempt.score DESC;

Can I convert this to a sub query driven query? As it stands it returns over a million rows due to LEFT JOIN

Here is the Query simplified:
SELECT
`Node`.`id`,
`Node`.`name`
FROM
`localhost`.`nodes` AS `Node`
LEFT JOIN `localhost`.`data_date_times` AS `DataDateTime` ON (
`DataDateTime`.`node_id` = `Node`.`id`
)
LEFT JOIN `localhost`.`data_locations` AS `DataLocation` ON (
`DataLocation`.`node_id` = `Node`.`id`
)
Are there any down sides to this VS a left join? Can I order the results for the overall query by returned values from the subqueries?
My stab at formatting this query:
SELECT
`Node`.`id`,
`Node`.`name`,
(SELECT `data_locations`.`name` FROM `data_locations` WHERE `data_locations`.`node_id` = `Node`.`id`),
(SELECT `data_date_times`.`name` FROM `data_date_times` WHERE `data_date_times`.`node_id` = `Node`.`id`)
FROM
`localhost`.`nodes` AS `Node`
WHERE ???
ORDER BY ???

Counting rows from a big mysql query (zend)

I a developing in zend and have a rather large mysql query. The query works fine and i get the list I expect. I am doing this using Select->Where.... below is the query.
SELECT DISTINCT `d`.* FROM `deliverable` AS `d` INNER JOIN `groups` AS `g1` ON d.id = g1.deliverable_id INNER JOIN `groupmembers` AS `gm1` ON g1.id = gm1.group_id LEFT JOIN `connection` AS `c` ON d.id = c.downstreamnode_id LEFT JOIN `deliverable` AS `d1` ON c.upstreamnode_id = d1.id INNER JOIN `deliverable` AS `d2` ON CASE WHEN d1.id IS NULL THEN d.id ELSE d1.id END = d2.id INNER JOIN `groups` AS `g` ON d2.id = g.deliverable_id INNER JOIN `groupmembers` AS `gm` ON g.id = gm.group_id WHERE (g1.group_type = 100) AND (gm1.member_id = 1) AND (c.downstreamnode_id IS NULL OR d.restrict_access = 1) AND (g.group_type = 100 OR g.group_type = 110) AND (gm.member_id = 1) AND (d.deliverable_type = 110 OR d.deliverable_type = 100) GROUP BY CASE WHEN c.downstreamnode_id IS NULL THEN d.id ELSE c.downstreamnode_id END
Only problem is when I try to count the rows in a mysql query I only get 1 returned. below is the query
SELECT DISTINCT count(*) AS `rowCount` FROM `deliverable` AS `d` INNER JOIN `groups` AS `g1` ON d.id = g1.deliverable_id INNER JOIN `groupmembers` AS `gm1` ON g1.id = gm1.group_id LEFT JOIN `connection` AS `c` ON d.id = c.downstreamnode_id LEFT JOIN `deliverable` AS `d1` ON c.upstreamnode_id = d1.id INNER JOIN `deliverable` AS `d2` ON CASE WHEN d1.id IS NULL THEN d.id ELSE d1.id END = d2.id INNER JOIN `groups` AS `g` ON d2.id = g.deliverable_id INNER JOIN `groupmembers` AS `gm` ON g.id = gm.group_id WHERE (g1.group_type = 100) AND (gm1.member_id = 1) AND (c.downstreamnode_id IS NULL OR d.restrict_access = 1) AND (g.group_type = 100 OR g.group_type = 110) AND (gm.member_id = 1) AND (d.deliverable_type = 110 OR d.deliverable_type = 100) GROUP BY CASE WHEN c.downstreamnode_id IS NULL THEN d.id ELSE c.downstreamnode_id END
i generate this from by using the same 'select' that generated the first query but I reset the columns and add count in.
$this->getAdapter()->setFetchMode(Zend_Db::FETCH_ASSOC);
$select
->reset( Zend_Db_Select::COLUMNS)
->columns(array('count('.$column.') as rowCount'));
$rowCount = $this->getAdapter()->fetchOne($select);
This method works fine for all my other queries only this one i am having trouble with. I suspect it has something to do the 'CASE' I have in there but it is strange because I am getting the correct rows the the first query. Any ideas. Thanks.
FYI below are two queries that I have working successfully.
SELECT DISTINCT `po`.* FROM `post` AS `po` INNER JOIN `postinfo` AS `p` ON po.postinfo_id = p.id WHERE (p.creator_id = 1) ORDER BY `p`.`date_created` DESC
SELECT DISTINCT count(*) AS `rowCount` FROM `post` AS `po` INNER JOIN `postinfo` AS `p` ON po.postinfo_id = p.id WHERE (p.creator_id = 1) ORDER BY `p`.`date_created` DESC
In this one I have 4 rows returned in the first query and 'int 4' returned for the second one. Does anyone know why it doesnt work for the big query?
Move your DISTINCT.
SELECT COUNT(DISTINCT `po`.*) AS `rowCount` ...
Ok figured it out It was the GROUP BY that was causing only 1 result to be returned. Thanks Interrobang for you help I am sure that using DISTINCT incorrectly will have caused me a headache in the future.
Try using SQL_CALC_FOUND_ROWS in your query?
http://dev.mysql.com/doc/refman/5.0/en/information-functions.html#function_found-rows
Using SQL_CALC_FOUND_ROWS is mysql-specific, but it's pretty nice for getting a full record count even when your initial query contains a limit. Once you get the count, don't include SQL_CALC_FOUND_ROWS in subsequent queries for extra records since that will cause extra load on your query.
Your initial query would be:
SELECT SQL_CALC_FOUND_ROWS DISTINCT `d`.* FROM `deliverable` AS `d` INNER JOIN `groups` ...
You'll have to do a subsequent call after your initial query executes to get the count by doing a SELECT FOUND_ROWS().
If you do a little searching, you'll find someone who extended Zend_Db_Select to include this ability.