MySql on what cols should I put indexes? - mysql

I have this query:
SELECT Concat(f.name, ' ', f.parent_names) AS FullName,
stts.name AS 'Status',
u.name AS Unit,
city.name AS City,
(SELECT Group_concat(c.mobile1)
FROM contacts c
WHERE c.id = f.husband_id
OR c.id = f.wife_id) AS MobilePhones,
f.phone AS HomePhone,
f.contact_initiation_date AS InitDate,
f.status_change_date AS StatusChangeDate,
cmt.created_at AS CommentDate,
cmt.comment AS LastComment,
f.reconnection_date AS ReconnectionDate,
(SELECT Group_concat(t.name, ' ')
FROM taggings tgs
JOIN tags t
ON tgs.tag_id = t.id
WHERE tgs.taggable_type = 'family'
AND tgs.taggable_id = f.id) AS HandlingStatus
FROM families f
JOIN categories stts
ON f.family_status_cat_id = stts.id
JOIN units u
ON f.unit_id = u.id
JOIN categories city
ON f.main_city_cat_id = city.id
LEFT JOIN comments cmt
ON f.last_comment_id = cmt.id
WHERE 1 = 0
OR ( u.is_busy = 1 )
OR ( f.family_status_cat_id = 1423 )
OR ( f.family_status_cat_id = 1422
AND f.status_change_date BETWEEN '2011-03-21' AND '2012-03-13' )
My problem is very specific. It is regarding the line:
SELECT GROUP_CONCAT( c.mobile1 )
FROM contacts c
WHERE c.id = f.husband_id
OR c.id = f.wife_id
) AS MobilePhones
When I use EXPLAIN, it seems that this query is bad. I get for this table (c = contacts): 38307 rows.
On what columns should I put the index according to the query?
I tried mobile1 - but no improvement (BTW - family_id is indexed in the contacts table).
I attach the image of the explain result:
Or maybe someone can help me optimize the query...

Any column you'll be searching on, to speed up the process. Keep in mind that keys are already indexed.

Well, it seems that using the GROUP_CONCAT is the problem.
I just seperated the wife and husband mobile to be 2 different columns.
First, I thought that using the GROUP_CONCAT will be faster, but it proved to be VERY WRONG.

Just out of my curiosity, what is the performance of the query
SELECT GROUP_CONCAT( c.mobile1 )
FROM contacts c
WHERE c.id IN(f.husband_id, f.wife_id)
) AS MobilePhones

Related

How can this SQL query be optimized? (Running on MySQL)

I need help on rewriting an SQL query that takes 26 seconds to run on an MySQL server.
The query is:
select
c.countries_name,
c.country_id,
(SELECT
count(1)
FROM
`fav_country`
WHERE
`country_id`=c.country_id
and device_id='".$device_id."'
) as isFav,
c.image,
c.countries_iso_code,
s.country
from
station s
left join countries c on c.country_id=s.country
where
isactive=:isactive
group by
s.country
I have tried rewriting it with two left/right joins but to no avail.
Basically, we have three tables, countries, fav_country and station, the common field is the country id (countries.country_id, fav_country.countr_id and station.country)
Thanks in advance!
This is the query you want optimized:
select c.countries_name, c.country_id,
(select count(1)
from fav_country fc
where fc.country_id = c.country_id and
fc.device_id = ? -- use a parameter!
) as isFav,
c.image, c.countries_iso_code
from station s left join
countries c
on c.country_id = s.country
where s.isactive = :isactive
group by s.country;
First, avoiding the outer aggregate is very helpful. I would replace it with exists:
select c.countries_name, c.country_id,
(select count(1)
from fav_country fc
where fc.country_id = c.country_id and
fc.device_id = ?
) as isFav,
c.image, c.countries_iso_code
from countries c
where exists (select 1
from station s
where s.country = c.country_id and
s.isactive = :isactive
);
Then for this query, you want indexes on:
station(country, isactive)
fav_country(country_id, device_id).

Why this query is to slow in mysql

I try to improve this query but I do not made successefully. I use a some left join and subquerys (I don't know another form) We try to get all bookings from users with certain status and the number of bookings multiples related with this booking and get from the log user the origin. The query is slow even if use a limit clausure. I Appreciate all the help can you give me.
This is the query:
SELECT DISTINCT b.uneaque_id, b.id, b.status, b.route_status, b.username, b.purpose, b.transfer, b.pickup_date, b.pickup_time, b.return_time, b.amount, b.default_location, b.start_address_route_comments, b.start_address_route, b.end_address_route_comments, b.end_address_route, u1.first_name, u1.last_name, b.transaction_Id, b.manual_payment, mr.AddressCount, lu.origin
FROM bookingdetails b
LEFT JOIN log_users lu ON lu.uneaque_id = b.uneaque_id AND lu.command_type = 'NEW BOOKING'
LEFT JOIN (
SELECT booking_id, COUNT(*) AS AddressCount FROM booking_multiple_rides GROUP BY booking_id
) mr ON b.id = mr.booking_id,
userdetails u1 WHERE b.username = u1.email
AND u1.user_status IN ('Active', 'Blocked', 'Not_Active')
AND b.default_location = 1
PD: Sorry for my english.
You have a ON b.id = mr.booking_id, userdetails u1 WHERE
you should change with a proper inner join
SELECT DISTINCT b.uneaque_id
, b.id, b.status
, b.route_status
, b.username
, b.purpose
, b.transfer
, b.pickup_date
, b.pickup_time
, b.return_time
, b.amount
, b.default_location
, b.start_address_route_comments
, b.start_address_route
, b.end_address_route_comments
, b.end_address_route
, u1.first_name
, u1.last_name
, b.transaction_Id
, b.manual_payment
, mr.AddressCount
, lu.origin
FROM bookingdetails b
LEFT JOIN log_users lu ON lu.uneaque_id = b.uneaque_id AND lu.command_type = 'NEW BOOKING'
LEFT JOIN (
SELECT booking_id
, COUNT(*) AS AddressCount
FROM booking_multiple_rides GROUP BY booking_id
) mr ON b.id = mr.booking_id
INNER JOIN userdetails u1 ON b.username = u1.email
AND u1.user_status IN ('Active', 'Blocked', 'Not_Active')
AND b.default_location = 1
and be sure you have proper index on
table bookingdetails a composite index on columns ( uneaque_id , id, default_location)
table log_users a composite index on columns (uneaque_id, command_type)
table userdetails a cmposite index on columns (email,user_status )
Tip 1.
Hiding a derived table in a LEFT JOIN is a prescription for sluggishness.
Replace
mr.AddressCount
with
( SELECT COUNT(*)
FROM booking_multiple_rides
WHERE booking_id = b.id
GROUP BY booking_id ) AS AddressCount
and get rid of the LEFT JOIN ( SELECT ... ) AS mr ON ..
Tip 2 Use explicit JOINs, no the old-fashioned "comma-join":
JOiN userdetails u1
ON b.username = u1.email
This won't help performance but it will make things clearer.
Tip 3: If you need an INNER JOIN (u1) after a LEFT JOIN, use parentheses. Else, put the inner joins first, then the left joins. This makes it easier to use, but may screw up the logic.
Tip 4: Don't use LEFT unless you need it. When you dont need it, it confuses the reader (and the Optimizer). (Again, no performance change.)
Tip 5: Why are you using DISTINCT? That takes an extra pass over all the resultset.
If those do not help enough, then provide SHOW CREATE TABLE so we can critique the indexes.

SQL Inner Query WHERE clause access to Outer Query tables

Good morning -
This is my first post here, after many years using SO as a very useful resource.
I've run into a problem with a complex (for me) query I'm pulling together for a wordpress site running woocommerce to process orders. I'm trying to add a filter to the order list which filters orders which contain products in a particular product category.
I'm afraid I've gotten in over my head with this query which joins a variety of meta tables on inner queries in order to get at the information I need in order to determine the product's category.
The problem is that I can't get the scoping rules to work in order to access required outer table information in the inner queries.
The query is:
SELECT SQL_CALC_FOUND_ROWS
wp_ot6q6i_posts.ID
FROM
wp_ot6q6i_posts
WHERE
1 = 1 AND YEAR(wp_ot6q6i_posts.post_date) = 2015 AND MONTH(wp_ot6q6i_posts.post_date) = 12 AND wp_ot6q6i_posts.post_type = 'shop_order' AND(
(
wp_ot6q6i_posts.post_status = 'wc-pending' OR wp_ot6q6i_posts.post_status = 'wc-processing' OR wp_ot6q6i_posts.post_status = 'wc-on-hold' OR wp_ot6q6i_posts.post_status = 'wc-completed' OR wp_ot6q6i_posts.post_status = 'wc-cancelled' OR wp_ot6q6i_posts.post_status = 'wc-refunded' OR wp_ot6q6i_posts.post_status = 'wc-failed'
)
) AND EXISTS(
SELECT
t2.PROD_ID
FROM
(
SELECT
wp_ot6q6i_woocommerce_order_itemmeta.meta_value AS PROD_ID
FROM
wp_ot6q6i_woocommerce_order_items
LEFT JOIN
wp_ot6q6i_woocommerce_order_itemmeta
ON
wp_ot6q6i_woocommerce_order_itemmeta.order_item_id = wp_ot6q6i_woocommerce_order_items.order_item_id
WHERE
wp_ot6q6i_woocommerce_order_items.order_item_type = 'line_item' AND wp_ot6q6i_woocommerce_order_itemmeta.meta_key = '_product_id' AND wp_ot6q6i_posts.ID = wp_ot6q6i_woocommerce_order_items.order_id
) t1
INNER JOIN
(
SELECT DISTINCT
wposts.ID AS PROD_ID
FROM
wp_ot6q6i_posts wposts
LEFT JOIN
wp_ot6q6i_postmeta wpostmeta
ON
wposts.ID = wpostmeta.post_id
LEFT JOIN
wp_ot6q6i_term_relationships
ON
(
wposts.ID = wp_ot6q6i_term_relationships.object_id
)
LEFT JOIN
wp_ot6q6i_term_taxonomy
ON
(
wp_ot6q6i_term_relationships.term_taxonomy_id = wp_ot6q6i_term_taxonomy.term_taxonomy_id
)
WHERE
wp_ot6q6i_term_taxonomy.taxonomy = 'product_cat' AND wp_ot6q6i_term_taxonomy.term_id IN(
SELECT
term_id
FROM
`wp_ot6q6i_terms`
WHERE
slug = 'preorder'
)
ORDER BY
wpostmeta.meta_value
) t2
ON
t1.PROD_ID = t2.PROD_ID
)
ORDER BY
wp_ot6q6i_posts.post_date
DESC
LIMIT 0, 20
And the error I'm getting is:
1054 - Unknown column 'wp_ot6q6i_posts.ID' in 'where clause'
Thanks all for your help. I ended up going in a different direction to solve this problem, one I'm more comfortable with as a dev...I'm pulling the fixed list of items from the last join and building a query in code that has a series of more simple queries in the where clause, thereby avoiding the whole Exists approach.
Thanks again for your help.

How to optimize this mysql query? Runs slow

I'm using a database that, imho, wasn't designed well, but maybe it's just me not understanding it. Anyways, I have a query that pulls the correct information, but it is really slowing down my php script. I was hoping someone could take a look at this and let me know if nesting queries to this depth is bad, and whether or not there is a way to simplify the query from the relationships depicted in the sql statement below.
SELECT name
FROM groups
WHERE id = (SELECT DISTINCT immediateparentid
FROM cachedgroupmembers
WHERE groupid = (SELECT g.id AS AdminCc
FROM Tickets t, groups g
WHERE t.Id = 124 AND t.id = g.instance AND g.type = 'AdminCc')
AND immediateparentid <> (SELECT g.id AS AdminCc
FROM Tickets t, groups g
WHERE t.Id = 124 AND t.id = g.instance AND g.type = 'AdminCc'))
Please help
Update:
Here is the output from using Explain
You may need to right click and select "View Image" for the text to be clear.
From what I can tell, you can eliminate one sub-select.
SELECT name
FROM groups
WHERE id = (
SELECT DISTINCT immediateparentid
FROM cachedgroupmembers
WHERE groupid = (
SELECT g.id
FROM Tickets t, groups g
WHERE t.Id = 124 AND t.id = g.instance AND g.type = 'AdminCc'
) AND immediateparentid != groupid
)
I'm much more used to PL/SQL on Oracle but I'll give it a try.
Get rid of aliases, you don't need them here.
Make sure columns used in the where clause are indexed (t.Id and g.type).
Don't know if MySQL indexes foreign keys by default but worth the check.
You can shorten your SQL code like that:
SELECT name
FROM groups
WHERE id = (
SELECT DISTINCT immediateparentid
FROM cachedgroupmembers
WHERE groupid = (
SELECT g.id
FROM Tickets t, groups g
WHERE t.Id = 124 AND t.id = g.instance AND g.type = 'AdminCc'
) AND immediateparentid != groupid
)
or:
SELECT name
FROM groups
WHERE id = (
SELECT DISTINCT immediateparentid
FROM cachedgroupmembers
WHERE groupid = (
SELECT g.id
FROM Tickets t inner join groups g on t.id = g.instance
WHERE t.Id = 124 AND g.type = 'AdminCc'
) AND immediateparentid != groupid
)
if your tickets table is big you may consider a temp table instead of querying it twice

Speedier alternative to SUBSTRING() in MySQL?

I have a query that uses SUBSTRING() as a criteria:
SELECT p.name p_name,
pa.line1 p_line1,
pa.zip p_zip,
c.name c_name,
ca.line1 c_line1,
ca.zip c_zip
FROM bank b
JOIN import_bundle ib ON ib.bank_id = b.id
JOIN generic_import gi ON gi.import_bundle_id = ib.id
JOIN account_import ai ON ai.generic_import_id = gi.id
JOIN account a ON a.account_import_id = ai.id
JOIN account_address aa ON aa.account_id = a.id
JOIN address ca ON aa.address_id = ca.id
JOIN address pa ON pa.zip = ca.zip OR (pa.zip = ca.zip AND pa.line1 = ca.line1)
JOIN prospect p ON p.address_id = pa.id
JOIN customer c ON a.customer_id = c.id
WHERE b.name = 'M'
AND ib.active = 1
AND gi.active = 1
AND SUBSTRING(p.name, 1, 12) = SUBSTRING(c.name, 1, 12)
LIMIT 100
As you can see, it's just comparing the first 12 characters of p.name and c.name. Unfortunately, adding this query to the WHERE clause makes my query unbearably slow. Are there any tricks out there to do this same comparison, or is my best bet to add another column to each table that contains the first 12 characters of the customer's name? I hope it's not the latter because that would be a lot of work and I'll ultimately be doing several comparisons like this.
Add the extra columns and set up an update trigger to populate them automatically. Be sure to create indexes on the new columns, of course.