SQL query to delete WP users with duplicate first and last names - mysql

We have a woocommerce site that has had a backdoor registration for years resulting in thousands of spam accounts.
I've been able to remove all of the ".ru" and other spam email accounts but there are many gmail addresses being used.
The one thing that seems to be common for many of these is that the first and last name are identical ex: "Donaldpat Donaldpat".
How would I write an SQL query that would delete the users from the wp_users table that have an identical value for first and last name in the related wp_usermeta table values?
would it be something like this:
DELETE FROM wp_users
WHERE EXISTS (
SELECT * FROM wp_usermeta
WHERE wp_usermeta.user_id = wp_users.ID
AND wp_usermeta.first_name = wp_usermeta.last_name
)

You can try this, what I'm doing here is separating the users.ID that I don't need so I just have the tables that I want to remove. Instead of using EXISTS I decided to use IN because that way you get the list ID directly, and remove them.
DELETE FROM wp_users
WHERE wp_users.ID IN (
SELECT DISTINCT user_id
FROM wp_usermeta
WHERE wp_usermeta.first_name = wp_usermeta.last_name
)
I hope this was helpful.

Related

Delete wp users based on role and registration date

I have about 180000 users in my wp_users database. Most of them are old and dormant. I fear this is slowing down my site (is this likely?). I want to safely delete old accounts along with all their associated data.
Using bulk delete plugins does not work as they timeout with so many users. I want to do it with mysql instead. By searching around I have found the following code:
DELETE
wp_users,
wp_usermeta
FROM
wp_users
INNER JOIN wp_usermeta ON wp_users.ID = wp_usermeta.user_id
WHERE
meta_key = 'wp_capabilities' AND
meta_value LIKE '%subscriber%' AND
user_registered < NOW() - INTERVAL 360 DAY
The code works but is it safe and is it the best way? Will it delete their associated meta data etc.
Please first take a backup of your database before running this query
Yes You can use this query to delete the user & user_meta data but if your theme/plugin saving users data in some others table(if you are using any plugin that store user data in other table) than you have to look again in database and modify your query according to this..

Create view in phpMyAdmin from three tables

I'm trying to create a view in MySQL, using phpMyAdimn, which will display data from three tables, in the format below. It needs to be sorted by last name, first name. I've never pulled data from more than one table before and this is difficult for me to grasp. I need to get this to work as soon as possible so I can create reports in Google Data Studio for our client.
Thanks...
View needs to present data in this order, and is named UserCourseProgress.
Last Name, First Name, Email, Group (Atlanta, Charleston, Greenvile, Nashville), Course Progress, Completion Date
Tables and columns in database.
TABLES COLUMNS
wp_users user_email
wp_usermeta first_name, last_name, wp_2_capabilities
wp_2_wpcw_user_progress unit_completed_status, unit_completed_date
Code I've tried.
CREATE VIEW UserCourseProgress AS
SELECT
wp_users.user_email, wp_usermeta.first_name, wp_usermeta.last_name, wp_usermeta.wp_2_capabilities, wp_2_wpcw_user_progress.unit_completed_status, wp_2_wpcw_user_progress.unit_completed_date
FROM
wp_users,
wp_usermeta,
wp_2_wpcw_user_progress
INNER JOIN wp_usermeta ON (wp_users.user_email=wp_usermeta.first_name) AND (wp_users.user_email=wp_usermeta.last_name) AND (wp_users.user_email=wp_usermeta.wp_2_capabilities)
INNER JOIN wp_2_wpcw_user_progress (wp_users.user_email=wp_2_wpcw_user_progress.unit_completed_status) AND (wp_users.user_email=wp_2_wpcw_user_progress.unit_completed_date)
I'm answering my own question here. Let me state that I'm a novice with MySQL queries, but some of you may have guessed that.
I studied many examples before I was able to get my query to work, and it took several hours, over several days. The code for my solution is as follows and it works. I still haven't figured out how to concatenate the data in wp_2_capabilities to only show the role, and not the serialized data, but that is for another day.
Thanks for the input, to the ones who did.
MY SOLUTION
CREATE VIEW CourseProgress AS
SELECT
wp_users.ID,
wp_users.user_email,
firstmeta.meta_value AS first_name,
lastmeta.meta_value AS last_name,
rolemeta.meta_value AS wp_2_capabilities,
unitprogress.unit_completed_status
FROM
wp_users
LEFT JOIN wp_usermeta AS firstmeta
ON
wp_users.ID = firstmeta.user_id AND firstmeta.meta_key = 'first_name'
LEFT JOIN wp_usermeta AS lastmeta
ON
wp_users.ID = lastmeta.user_id AND lastmeta.meta_key = 'last_name'
LEFT JOIN wp_usermeta AS rolemeta
ON
wp_users.ID = rolemeta.user_id AND rolemeta.meta_key = 'wp_2_capabilities'
LEFT JOIN wp_2_wpcw_user_progress AS unitprogress
ON
wp_users.ID = unitprogress.user_id AND unitprogress.unit_completed_status = 'complete'
WHERE
rolemeta.meta_value REGEXP 'gs_atlanta|gs_charleston|gs_greenvilee|gs_nashville'
ORDER BY
wp_users.ID;

MySQL : update with Where clause in subquery

I used to know how to do this but a lack of practice made me lose it.
I am trying to update usernames from a table by comparing matching email in another.
basically the first table has username empty, while the other has username and emails filled.
here is my wrong query :
UPDATE users SET username = (SELECT Username FROM clients WHERE email in mail)
email is from my clients table, mail is from my users table
I would suggest update with using JOIN with UPDATE, something like this should work
UPDATE users
INNER JOIN
#your relationship / for example
clients ON (users.id = clients.user_id)
SET
users.username = clients.email
WHERE
users.username IS NULL
Just make sure ON clause is correct relation that you have between users and clients and it should update all records in users username column with email from clients
You have not provided enough information to get a clear answer.
If you don't set a WHERE clause in your UPDATE statement, you will update all records with the same value, which is probably not what you want
If you want to update the users table with some info on matching records in the client table, then you need to join both tables on your matching field. Something like this :
UPDATE users U, clients C
SET u.username = c.username
WHERE U.email=C.email
AND C.email IN (...)
In this example I assumed that the email address was the matching field between the 2 tables (you should adapt this), and that you provide a list of target email addresses (you can remove this)

SQL Query for WordPress/WooCommerce in PHPmyAdmin to select spam users

I have a WordPress database that has approximatly 28000 spam user`registrations in there which I some how need to isolate and delete without removing any actual customers.
I have noticed that all of the spam user registrations have an empty meta_value for a specific meta_key
The meta_key in question is billing_first_name
Therefore, I am trying to write a query I can execute in PHPmyAdmin that will return a list of all user IDs that have an empty meta_value for the meta_key billing_first_name so that I can then delete all of these users from the _usermeta table AND the _users table.
I found a user online who had a similar issue: https://wordpress.org/support/topic/deleting-spam-user-accounts-from-site-with-woocommerce
He wrote the following query that I have attempted to run on my database:
SELECT * FROM wp_usermeta JOIN wp_users ON (wp_usermeta.user_id = wp_users.ID) WHERE user_id NOT IN (SELECT um1.user_id FROM wp_usermeta um1 WHERE um1.meta_key = 'shipping_first_name')
However, its only returning 137 results, and with 28000+ plus registration, but only around 1000 actual orders, I have a heck of a lot more than 137 spam accounts in the database.
I have tried adapting the query to expand upon it and get it to do what I want, but I am not really a "database" person and am not having much luck.
My attempt was:
SELECT * FROM wv5_usermeta JOIN wv5_users ON (wv5_usermeta.user_id = wv5_users.ID) WHERE user_id NOT IN (SELECT um1.user_id FROM wv5_usermeta um1 WHERE um1.meta_key = 'billing_first_name' AND um1.meta_value IS NOT NULL)
Can someone help me formulate a working query that I can use?
Note that my attempt was based on the query I found online, and I have no idea if thats even the right approach for this.
My end goal is to identify ALL users that have never made any orders. Users that have made no orders do not have a billing address or shipping address set. So therefore, I am trying to build a query that selects users with a null billing address or null shipping address and then delete them.
Many Thanks for any asssitance provided.
EDIT:
Just to clarify, the site users WooCommerce and as such there is no specific orders table in the database. meta_keys such as "billing_first_name", "shipping_first_name" and such only get populated with a value once a user has made an order for the first time. Therefore, the assumption is that a spam user would have never made an order and thus these meta_keys will be null.
Just a guess according to this post woocommerce stores orders in a posts table with post_type = 'shop_order'.
So you can try this query :
SELECT u.* FROM wv5_users u
LEFT JOIN wv5_posts p
ON u.ID = p.post_author
AND p.post_type = 'shop_order'
GROUP BY u.ID
HAVING COUNT(p.id)=0
It should return list of users never had any posts with post_type = 'shop_order'. Probably you should check what exact post_type your woocommerce uses.
Be careful if you plan to delete all those users. Some of them could be your administrators.
EDIT Sorry, I have no wordpress and woocommerce installed. Since my query does not help. Let try to go your way but a bit properly:
SELECT u.* FROM wv5_users u
LEFT JOIN wv5_usermeta m
ON u.ID = m.user_id
AND m.meta_key = 'shipping_first_name'
GROUP BY u.ID
HAVING COUNT(m.id)=0
or according to this
HAVING COUNT(m.umeta_id)=0

Can you build a MySQL query to show not found results from the conditional

I am trying to find out how to find the emails that do not exist in a table using the emails from the conditional.
I could create a table with these emails but that seems like overkill for what I need it for.
What I am looking for is a query that would show me the conditional value and NULL as the user ID.
Is this possible?
I have a query like this:
SELECT u.uid, u.mail
FROM `users` u
WHERE u.mail IN (
'alot#of',
'emails#that',
'ineed#tofind',
)
This works great at finding the emails and associating the user id. Now I need to identify which emails do not exist in the result. I am currently only using 56 emails and 6 do not appear in the list. I am trying to identify which emails are not found.
NOT IN won't work as I have over 40,000 users. I only want to identify the emails not found from my conditional. I have 56 emails and only 50 results. I need to identify the 6 not found (they may not even be in the table at all)
Let me attempt to clarify this a little more:
I am given a list of emails for supposed accounts in the system. I am trying to find the accounts from the given email. This part is fine. Now, the issue I am having, I was given 56 emails but only 50 were found. I need to identify which emails out of the 56 were not found. The emails are all thrown into the conditional. NOT IN won't work because it would return all user but the 50 that were found. (roughly 40,000) I just need to identify the emails from the conditional that were not found in the table.
Thanks for any insight or suggestions to do what I need.
There isn't a way to do what you want without creating some additional items to track the emails. Basically, you're trying to get MySQL to tell you which items in the WHERE portion aren't found, but MySQL can only tell you about rows in a table.
You need to make a secondary table that stores the email addresses from your list, call it list. I would make it a single column table with just the emails. Then LEFT JOIN it against the users table and find where the uid is null.
SELECT u.uid, l.mail
FROM `list` l
LEFT JOIN `users` u ON u.mail=l.mail
WHERE u.uid IS NULL
As posted in the comments, NOT IN may be helpful. But there are also other ways. One of them is to left join your table with the result of your query and show only non-coincident rows:
select u.uid, u.mail
from users as u
left join (
select u.uid, u.mail
from users
where mail in ('alot#of','emails#that','ineed#tofind')
) as a on u.uid = a.uid
where a.uid is null;
Add the fields you need to the join (if uid is not enough)
So your question now becomes more complicated... you want to find all the E-Mails in your condition that are not found in your table.
As far as I know, there's not a simple SQL sentence that will give you that... but you can work with temp tables and get it. The solution implies:
Create a temporary table to hold the values you want to search (and add the appropriate indexes to it)
Insert the values you want to search
Execute a select query to find non-matching rows
So... let's do it:
-- 1. Create a temp table to hold the values
drop table if exists temp_search_values;
create temporary table temp_search_values (
mail varchar(100),
unique index idx_mail(mail) -- Don't allow duplicate values here
);
-- 2. Insert the search values
insert into temp_search_values (mail) values
('alot#of'),('emails#that'),('ineed#tofind');
-- 3. Execute the query
select a.*
from users as u
left join temp_search_values as a on u.mail = a.mail
where u.mail is null;
Remember: Temporary tables are only visible to the connection that created them, and are deleted when the connection is closed or killed.
NULL is a strange result. It's not true and it's not false. If you want to check for it, you have to look specifically.
SELECT u.uid, u.mail
FROM `users` u
WHERE u.mail NOT IN (
'alot#of',
'emails#that',
'ineed#tofind',
) and u.uid IS NULL
* Oh, I see what you're getting at. This will work, although it's not pretty. *
select * from
(SELECT 'emails#that' as v
UNION SELECT 'alot#of' as v,
UNION SELECT 'ineed#tofind' as v
) as test
left join users on u.mail = test.v
where u.uid is null