Need help in optimizing the query - mysql

SELECT
u.ID,
u.display_name as name,
u.user_email as email,
u.user_registered as registered,
(
select
meta_value
from
wp_usermeta
where
user_id = u.ID
and meta_key = 'mobileno'
limit
1
) as mobileno,
(
select
meta_value
from
wp_usermeta
where
user_id = u.ID
and meta_key = 'referral_id'
limit
1
) as referral_id,
(
SELECT
COUNT(meta_value) AS total_ref
FROM
wp_usermeta
WHERE
meta_key = 'ambassador_ref_id'
AND meta_value = referral_id
) as total_ref,
wc.task_no,
wc.status,
wc.uploaded_date,
wc.reject_reason
FROM
wp_users u,
wp_ca_tasks wc
WHERE
u.ID = wc.user_id
GROUP BY
wc.user_id,
wc.task_no;
In the above code, if we remove the block
(
SELECT
COUNT(meta_value) AS total_ref
FROM
wp_usermeta
WHERE
meta_key = 'ambassador_ref_id'
AND meta_value = referral_id
) as total_ref
the code executes a bit faster. But if we add that block, it basically gets stuck in Loading...
Currently using MySQL 5.7.
How can I optimize the above block of code to make the execution faster?

Ah, the notorious WordPress meta-table slowdown.
Change the comma-joins (FROM a,b WHERE a.ID = b.user_id) to proper JOINs.
Eliminate your dependent subqueries and replace them with JOINed subqueries.
A quicker query might look like this.
SELECT
u.ID,
u.display_name as name,
u.user_email as email,
u.user_registered as registered,
/* from the joined tables
mobilno.meta_value as mobileno,
referral_id.meta_value as referral_id,
counts.total_ref
wc.task_no,
wc.status,
wc.uploaded_date,
wc.reject_reason
FROM
wp_users u
JOIN wp_ca_tasks wc ON u.ID = wc.user_id
LEFT JOIN wp_usermeta mobilno ON mobilno.user_id = u.ID
AND meta_key = 'mobilno'
LEFT JOIN wp_usermeta referral_id ON referral_id.user_id = u.ID
AND meta_key = 'referral_id'
LEFT JOIN (
SELECT COUNT(*) total_ref,
meta_value referral_id
FROM wp_postmeta
WHERE meta_key = 'ambassador_ref_id'
GROUP BY meta_value)
) counts ON counts.referral_id = referral_id.meta_value
GROUP BY wc.user_id, wc.task_no;
The trick is to avoid repeating the queries buried in the SELECT statement over and over. LEFT JOINing them helps.
And, your WordPress tables need better indexes. Look at this. https://wordpress.org/plugins/index-wp-mysql-for-speed/

In addition to what O.Jones says, wc needs
INDEX(user_id, task_no)
However, the GROUP BY probably violates "only_full_group_by". That is, for a given user_id and task_no, you will get random values for other columns fetched from wp_ca_tasks.

Related

SQL - how to remove whole row if one of the column in subquery return NULL

I am stuck in 1 SQL query
SELECT u.*,
um2.meta_value as parent_user_id,
( select u.user_email FROM wp_users u WHERE u.ID = um2.meta_value ) AS parent_user_email
FROM
wp_users u
JOIN wp_usermeta um2 ON u.ID = um2.user_id
AND um2.meta_key = 'parent_user_id'
GROUP BY
u.ID
This query return 4 row ( As shown in the screenshot )
I want a scenario like : If subquery return NULL , then the whole row will not be shown.
So in this example "childthree" should not be shown , as "parent_user_email" is NULL , so the whole 3rd row need to remove
Use a join instead:
SELECT u.*, um2.meta_value as parent_user_id,
u2.user_email as parent_user_email
FROM wp_users u JOIN
wp_usermeta um2
ON u.ID = um2.user_id AND
um2.meta_key = 'parent_user_id' JOIN
wp_users u2
ON u2.ID = um2.meta_value
GROUP BY u.ID;
Note: This assumes that the email value itself is never NULL. If that is possible, add WHERE u2.user_email IS NOT NULL.
Also, your query should fail because the GROUP BY columns are inconsistent with the SELECT. However, logically it seems ok, because there is only one parent and user email per user. However, I would include those columns in the GROUP BY.

SQL for WP to delete users with multiple meta keys and comments

I need help putting together an SQL that can to remove users if they don't have (metakey1 or metakey2) and (does not have comments)
I have this SQL which does it for single meta_key
SELECT *
FROM wp_users LEFT JOIN wp_usermeta
ON wp_users.ID = wp_usermeta.user_id
AND wp_usermeta.meta_key = 'metakey1'
WHERE wp_usermeta.user_id IS NULL
How can i extend the above SQL to do that?
You can use in in the on clause:
SELECT u.*
FROM wp_users u LEFT JOIN
wp_usermeta um
ON u.ID = um.user_id AND
um.meta_key IN ('metakey1', 'metakey2', 'comments')
WHERE um.user_id IS NULL;
You get no matches only if all the metakeys are missing, which I think is what you are asking for.
EDIT:
You seem to want:
SELECT u.*
FROM wp_users u
WHERE NOT EXISTS (SELECT 1
FROM wp_usermeta um
WHERE u.ID = um.user_id AND
um.meta_key IN ('metakey1', 'metakey2')
) AND
NOT EXISTS (SELECT 1
FROM wp_comments c
WHERE u.ID = c.user_id
);
I prefer NOT EXISTS if you are going to have multiple comparisons to different tables.
Try doing a delete with an exists clause asserting the requirement of either of two keys:
DELETE
FROM wp_users wp1
WHERE NOT EXISTS (SELECT 1 FROM wp_usermeta wp2
WHERE wp1.ID = wp2.user_id AND
wp2.meta_key IN ('metakey1', 'metakey2'));
Note that the following where clause is no longer needed in the version of the query I wrote above:
WHERE wp_usermeta.user_id IS NULL
It is no longer needed because NOT EXISTS now handles the job which the exclusion join was handling previously.

Reduce sql Query execution time from 3.349s

This query is working perfectly but uses 3.3493s to execute, please help me check through it to see if there is a way to better optimize it.
SELECT u.ID,
(SELECT meta_value FROM wpcg_usermeta WHERE meta_key = 'first_name' AND user_id = u.ID ) AS firstname,
(SELECT meta_value FROM wpcg_usermeta WHERE meta_key = 'last_name' AND user_id = u.ID ) AS lastname,
(SELECT meta_value FROM wpcg_usermeta WHERE meta_key = 'user_avatar_thumb' AND user_id = u.ID ) AS avatarurl,
(SELECT COUNT(m.user_id) FROM wpcg_usermeta AS m , wpcg_users AS uz WHERE m.user_id = uz.ID AND m.meta_key = 'user_parent' AND m.meta_value = u.ID AND u.user_registered BETWEEN DATE_SUB(NOW(), INTERVAL 7 DAY) AND NOW()) AS referral_users
FROM wpcg_users AS u
WHERE (SELECT COUNT( user_id ) FROM wpcg_usermeta WHERE meta_key = 'user_parent' AND meta_value = u.ID ) > 0
ORDER BY referral_users DESC LIMIT 15 OFFSET 0
Consider restructuring your query
Select col1, col2, col3, ...
FROM wpcg_users AS u
JOIN wpcg_usermeta m on u.ID = m.user_id
where meta_key in ('first_name', 'last_name', ...)
You have a number of subqueries which can be eliminated through re-structuring the query which can improve performance. When you have a sub query like this in the select it is run for every row.
First of all, provide meta of used table and type of database- columns, indexes, type of tables and etc (its look like as MySql, but its may be false). Try split this request on some simple requests. Then try to get statistics about each requests.
Also try reduce calls to some table, doing nested sql-c.
Sorry for my English. I’m studying it at now)

mysql inner join on two tables

Columns in table wp_users:
id
user_login
Columns in table wp_usermeta:
user_id
meta_key [if equals 'primaryblog']
metav_value
The id in wp_users and the user-id in wp_usermeta are same. I am expecting the result as
id, user_login, meta_key, meta_value
I tried:
select a.user_id,a.meta_key,a.meta_value
from wp_usermeta as a
where meta_key = 'primaryblog'
inner join b.id, b.user_login
from wp_users as b on a.user_id=b.id
How to get the intended result?
The JOIN comes before the WHERE clause:
SELECT
a.id, a.user_login, b.meta_key, b.meta_value
FROM wp_users a
JOIN wp_usermeta b ON a.id = b.user_id
WHERE meta_key = 'primaryblog';

MySQL subquery in where clause

I have a MySQL query which goes like this:
select
*,
(select meta_value
from nord_usermeta m
where meta_key = 'firm' and user_id = s.user_id
limit 1)
as firm,
(select meta_value f
rom nord_usermeta m
where meta_key = 'first_name' and user_id = s.user_id
limit 1)
as first_name,
(select meta_value
from nord_usermeta m
where meta_key = 'last_name' and user_id = s.user_id
limit 1)
as last_name
from nord_submissions s
order by created_at desc
Now I need to narrow it down with a where clause like this:
select
*,
(select meta_value
from nord_usermeta m
where meta_key = 'firm' and user_id = s.user_id
limit 1)
as firm,
(select meta_value
from nord_usermeta m
where meta_key = 'first_name' and user_id = s.user_id
limit 1)
as first_name,
(select meta_value
from nord_usermeta m
where meta_key = 'last_name' and user_id = s.user_id
limit 1)
as last_name
from nord_submissions s
where firm like '%DG%'
order by created_at desc
The problem obviously is in way i use subquery but i cant find a way to reference it in where clause.
Please help.
Why go so Long...try with SELF JOIN ( joining same tables in query )..see below (not tested)
SELECT ns.*,
um1.meta_value AS firmName,
um2.meta_value AS firstName,
um3.meta_value AS lastName
FROM nord_submission ns
INNER JOIN nord_usermeta um1 USING(id) AND meta_key = 'firm'
INNER JOIN nord_usermeta um2 USING(id) AND meta_key = 'first_name'
INNER JOIN nord_usermeta um3 USING(id) AND meta_key = 'last_name'
WHERE um1.meta_value LIKE '%DG%'
ORDER BY ns.created_at
Note: if both table have different column name , by which u refrence each other, then you can replace USING(id) with ns.user_id=um1.user_id and so on
If you're going to use keys rather than columns you'll have to let the application sort it out. Using so many sub-queries you'll see a performance hit.
SELECT *
FROM nord_submissions
INNER JOIN nord_usermeta
USING (user_id)
WHERE (meta_key='last_name' or meta_key='first_name' or (meta_key='firm' and meta_value LIKE '%DG%') )
ORDER BY created_at desc