Reputation: 5986
I have about 30k products that I auto imported to WooCommerce from an API. There was a bug with the API that ended up adding duplicate products for most of the items, but now I am left with 30k+ products instead of 15k products.
There is no way I can go through and delete every duplicate manually, I have been trying to come up with a SQL script that could do this but due to all of the different data stored in WordPress it makes it complex.
It seems like this script gets all the unique rows, but not the duplicates.
SELECT meta_value, meta_key, count(*)
FROM wp_postmeta WHERE meta_key LIKE '_sku'
GROUP BY `meta_value`
So what I am looking to do is to find all rows with the column of meta_key = _sku
with duplicate rows of the value in the column called meta_value
So for example if I have some rows like this:
meta_id post_id meta_key meta_value
1504098 57049 _sku 26785030612
1504135 57051 _sku 26785030612
1503993 57045 _sku 26785033309
I want it to look like this:
meta_id post_id meta_key meta_value
1504135 57051 _sku 26785030612
1503993 57045 _sku 26785033309
So I need to delete the first row just based on the value in meta_value.
Upvotes: 4
Views: 5878
Reputation: 11
Recently I resolved delete duplicate SKUs records with multiple permalinks ended with -2
or any digit at end containing duplicate URLs for same product Issue for WordPress site.
Query to get all duplicate SKUs and their all duplicate URLs / permalinks:
SELECT a.post_name,a.post_title,a.ID,b.meta_value as _sku FROM wp_posts a
INNER JOIN (SELECT meta_value,max(post_id) AS post_id FROM wp_postmeta WHERE meta_key='_sku'
GROUP BY meta_value HAVING COUNT(meta_value) > 1 ) b ON a.ID=b.post_id WHERE post_type = 'product' AND post_status = 'publish';
Query to Delete all duplicate SKU's and their all duplicate URLs / permalinks:
DELETE FROM wp_posts WHERE id IN
(
SELECT a.post_id FROM
(SELECT meta_value,max(post_id) AS post_id FROM wp_postmeta
WHERE meta_key='_sku'
GROUP BY meta_value HAVING COUNT(meta_value) > 1 ) a
WHERE post_type = 'product'
AND post_status = 'publish'
);
Upvotes: 1
Reputation: 39
in my Case, I need to delete all products having duplicate SKU value so I ran this query 4 to 5 times and my problem was solved
DELETE FROM e78_posts WHERE id IN (SELECT a.post_id FROM ( SELECT MIN(meta_id) AS `meta_id` ,MIN(post_id) AS `post_id` FROM e78_postmeta WHERE meta_key='_sku' GROUP BY meta_value HAVING COUNT(*) > 1 ) a )
DELETE FROM e78_postmeta WHERE meta_id IN (SELECT a.meta_id FROM ( SELECT MIN(meta_id) AS `meta_id` ,MIN(post_id) AS `post_id` FROM e78_postmeta WHERE meta_key='_sku' GROUP BY meta_value HAVING COUNT(*) > 1 ) a )
Upvotes: 0
Reputation: 3466
I just solved this problem for a friend. We were trying to delete duplicate SKUs for a WordPress site that is using the WooCommerce plugin.
Here is a query to find all duplicate SKUs and their ids:
# CHECK QUERY
# Use this query to get a list of post_ids and old and new skus...
# we wanted to keep the latest sku so we wanted to delete
# the older duplicate meta records
select * from (
select post_id, min(meta_id) as deletethis, max(meta_id) as keepthis, meta_value, meta_key, count(*) as c from wp_postmeta
where meta_key like '_sku'
and meta_value is not null
and meta_value != ''
group by `meta_value`
) as skus where c > 1
Here is a query to delete the duplicate SKU ids. We wanted the query to delete the duplicates so that any non-sku related meta entries were un-affected.
# DELETE QUERY
# Use this query to delete the older sku record and keep
# the newer sku record
delete from wp_postmeta where meta_id in (
select skus.deletethis from (
select min(meta_id) as deletethis, max(meta_id) as keepthis, meta_value, meta_key, count(*) as c from wp_postmeta
where meta_key like '_sku'
and meta_value is not null
and meta_value != ''
group by `meta_value`
) as skus where c > 1
);
Upvotes: 5
Reputation: 30819
If meta_id
is unique then you can use the following query to SELECT
all the (minimum) meta_id
values for duplicate meta_value
, e.g.:
SELECT MIN(meta_id)
FROM wp_postmeta
GROUP BY meta_value
HAVING COUNT(*) > 1;
You can then wrap this query into DELETE
to delete the rows, e.g.:
DELETE FROM wp_postmeta
WHERE meta_id IN (
SELECT a.meta_id FROM (
SELECT MIN(meta_id) AS `meta_id`
FROM wp_postmeta
GROUP BY meta_value
HAVING COUNT(*) > 1
) a
);
Upvotes: 3