I have some duplicated rows on my Wordpress wp_postmeta table. Actually hundreds of cases where old postmeta data are listed 2 or 3 times... maybe from some data import process done in the past... So I need to remove unneeded duplicate rows from wp_postmeta table, leaving just the ones with higher meta_id number... To exemplify what the wp_postmeta table looks like:
meta_id | post_id | meta_key | meta_value
155153 | 177115 | owner_img | https://www.example.com/a.jpg
176231 | 177115 | owner_img | https://www.example.com/a.jpg
193983 | 177115 | owner_img | https://www.example.com/a.jpg
Note that these are 3 metadata for the same post on wp_post table (as it has the same post_id)... so I just need to keep the latest metadata row, and delete all other instance where metadata is duplicated for each meta_key... how can I do that?
DELETE wp_postmeta.*
FROM wp_posts
INNER JOIN wp_postmeta ON wp_postmeta.post_ID = wp_posts.ID
I was able to figure it out after many research, in case anyone out there is looking for the answer...
DELETE t1 FROM wp_postmeta t1
INNER JOIN wp_postmeta t2
WHERE t1.meta_id < t2.meta_id
AND t1.meta_key = t2.meta_key
AND t1.post_id=t2.post_id;
You can do the deletion by first filtering out the lower valued meta_id
DELETE wp_posts
FROM wp_posts
JOIN wp_posts x ON x.post_ID = wp_posts.post_ID
AND wp_posts.meta_id < x.meta_id
WHERE x.post_id=wp_posts.post_id
AND x.meta_key = wp_posts.meta_key
exercise:
create table wp_posts (meta_id integer,
post_id integer,
meta_key varchar(20),
meta_value varchar(200)
);
insert into wp_posts values(155153 , 177115 , 'owner_img','https://www.example.com/a.jpg');
insert into wp_posts values(176231 , 177115 , 'owner_img' , 'https://www.example.com/a.jpg');
insert into wp_posts values(193983 , 177115 , 'owner_img' , 'https://www.example.com/a.jpg');
commit;
-- SELECT TO CHECK/VERIFY
Select distinct wp_posts.meta_id
FROM wp_posts
JOIN wp_posts x ON x.post_ID = wp_posts.post_ID
AND wp_posts.meta_id < x.meta_id
WHERE x.post_id=wp_posts.post_id
AND x.meta_key = wp_posts.meta_key
OUTPUT
meta_id
155153
176231
Delete
Delete wp_posts
FROM wp_posts
JOIN wp_posts x ON x.post_ID = wp_posts.post_ID
AND wp_posts.meta_id < x.meta_id
WHERE x.post_id=wp_posts.post_id
AND x.meta_key = wp_posts.meta_key
;
commit;
Select * from wp_posts;
OUTPUT
meta_id post_id meta_key meta_value
193983 177115 owner_img https://www.example.com/a.jpg
Related
I cannot find the meta key for the stock quantity in Woocommerce. What would be the best way to update the stock quantity in Woocommerce using MYSQL?
it's more secure you use the PHP functions. But if you want to update using sql, you can try this:
Checking stock
SELECT * FROM olenka.wp_postmeta where meta_key = '_stock';
Checking stock status:
SELECT post_id AS id, meta_value AS stockstatus FROM wp_postmeta WHERE meta_key = '_stock_status'
Set manage stock true:
UPDATE wp_postmeta SET meta_value = 'yes' WHERE meta_key = '_manage_stock'
Set the 'instock' flag:
UPDATE wp_postmeta set meta_value = 'instock' WHERE meta_key = '_stock_status' AND meta_value = 'outofstock'
Update the stock
UPDATE wp_postmeta SET meta_value = 99 WHERE meta_key = '_stock' AND meta_value IS NULL;
OR
UPDATE wp_postmeta SET meta_value = 99 WHERE meta_key = '_stock' AND meta_value > 0;
I have this query:
SELECT *
FROM `wp_postmeta`
WHERE `meta_key` = '_test'
AND `post_id` IN (SELECT post_id FROM `wp_postmeta`
where meta_value = 8023)
Returns the SQL error:
Table 'wp_postmeta' is specified twice, both as a target for 'UPDATE'
and as a separate source for data
I have read other answers and attempting to add a further SELECT * FROM ( ) around the sub query but didn't help.
I assume I need some form of AS in here but can't figure out the exact code.
Can you rewrite the query in the format that won't trigger the error?
The same for this similar query:
UPDATE wp_postmeta
SET meta_value = 5.55
WHERE meta_key = '_regular_price'
AND post_id IN (
SELECT post_id
FROM wp_postmeta
WHERE meta_value = 8023
)`
You can use alias for table name
(and as suggestion in your case you can also use join instead of in )
SELECT a.*
FROM `wp_postmeta` a
inner join `wp_postmeta` b on a.`post_id = b.post_id
where a.`meta_key` = '_test'
and b.meta_value = 8023
In update you could use a join with subselect for circumvent the limits due to update actions on the same table
UPDATE wp_postmeta a
inner join (
SELECT post_id
FROM wp_postmeta
WHERE meta_value = 8023
) t on a.`post_id = t.post_id and a.`meta_key` = '_test'
SET meta_value = 5.55
SELECT * FROM wp_postmeta as wp_out
WHERE wp_out.meta_key = '_test'
AND wp_out.post_id IN
( SELECT wp_in.post_id
FROM wp_postmeta as wp_in
where wp_in.meta_value = 8023)
Although this is not a good answer, but it is effective, ha ha..
SELECT *
FROM wp_postmeta
WHERE meta_key = '_test'
AND post_id IN (SELECT GROUP_CONCAT(post_id) FROM wp_postmeta
where meta_value = 8023)
The SELECT query posted in the question is equivalent to this one:
SELECT p1.*
FROM `wp_postmeta` p1
INNER JOIN `wp_postmeta` p2 ON p1.`post_id` = p2.`post_id`
WHERE p1.`meta_key` = '_test'
AND p2.`meta_value` = 8023
In fact, if some conditions are met, the MySQL engine converts the original SELECT query into a query similar to this as an optimization.
This SELECT query can be easily changed into the desired UPDATE query:
UPDATE `wp_postmeta` p1
INNER JOIN `wp_postmeta` p2 ON p1.`post_id` = p2.`post_id`
SET p1.`meta_values` = 5.55
WHERE p1.`meta_key` = '_test'
AND p2.`meta_value` = 8023
I'm looking to achieve my goals as described below using one single query, as opposed to multiple queries as I currently have to use.
The problem I am having is that data from the wp_usermeta table is stored as a meta_key/meta_value in 2x columns, as opposed to each type of data having it's own column. While the reasoning for this makes sense, it does mean I'm stumped at this point.
The database in question is for a WordPerss site, should anyone wish to replicate this.
My goals
For each user who has at least one image/video post I need to grab the following details -
+-----------------------+---------------+------------------------------------------------+
| Description | Table | Column |
+-----------------------+---------------+------------------------------------------------+
| User ID | `wp_users` | `ID` |
| Display Name | `wp_users` | `display_name` |
| First Name | `wp_usermeta` | `meta_value` (WHERE `meta_key` = 'first_name' |
| Description | `wp_usermeta` | `meta_value` (WHERE `meta_key` = 'description' |
| Facebook profile link | `wp_usermeta` | `meta_value` (WHERE `meta_key` = 'facebook' |
| Google+ profile link | `wp_usermeta` | `meta_value` (WHERE `meta_key` = 'google_plus' |
| Twitter profile link | `wp_usermeta` | `meta_value` (WHERE `meta_key` = 'twitter' |
+-----------------------+---------------+------------------------------------------------+
My current solution
First I select the ID and display name of all users who have at least 1 image/video post (this is one single query) -
SELECT DISTINCT `wp_users`.`ID`, `wp_users`.`display_name`
FROM `wp_posts`
INNER JOIN `wp_users`
WHERE `wp_posts`.`post_type` = "attachment"
AND `wp_posts`.`post_status` = "inherit"
AND `wp_posts`.`post_author` = `wp_users`.`ID`
AND (
`wp_posts`.`post_mime_type` LIKE "image%"
OR `wp_posts`.`post_mime_type` LIKE "video%"
)
Next, I have to loop through each result from the first query and select the first name, description and social media links for each (this is one example, for user_id = 2) -
SELECT `wp_usermeta`.`meta_key`, `wp_usermeta`.`meta_value`
FROM `wp_usermeta`
WHERE `wp_usermeta`.`user_id` = 2
AND (
`wp_usermeta`.`meta_key` = 'first_name'
OR `wp_usermeta`.`meta_key` = 'description'
OR `wp_usermeta`.`meta_key` = 'facebook'
OR `wp_usermeta`.`meta_key` = 'google_plus'
OR `wp_usermeta`.`meta_key` = 'twitter'
);
As part of the loop where the second query is run I also have to insert those results into the results from the first. All of this not only means extra code, but leads to a longer execution time.
My full code can be found here - http://pastebin.com/P2jv3WTt
Is this goal achievable with MySQL, or is it simply not something it is able to do? Thanks.
What I've tried
I have tried to join to the wp_usermeta table as follows, outputting the results as a named column, but there is an issue - the only results are for users who have an entry for every single meta_key in the wp_usermeta table, but some don't (no Twitter profile link, for example).
SELECT DISTINCT
u.`ID`,
u.`display_name`,
m1.`meta_value` AS first_name,
m2.`meta_value` AS description,
m3.`meta_value` AS facebook,
m4.`meta_value` AS google_plus,
m5.`meta_value` AS twitter
FROM `wp_users` u
JOIN `wp_posts` p
JOIN `wp_usermeta` m1 ON (m1.user_id = u.id AND m1.meta_key = 'first_name')
JOIN `wp_usermeta` m2 ON (m2.user_id = u.id AND m2.meta_key = 'description')
JOIN `wp_usermeta` m3 ON (m3.user_id = u.id AND m3.meta_key = 'facebook')
JOIN `wp_usermeta` m4 ON (m4.user_id = u.id AND m4.meta_key = 'google_plus')
JOIN `wp_usermeta` m5 ON (m5.user_id = u.id AND m5.meta_key = 'twitter')
WHERE p.`post_type` = "attachment"
AND p.`post_status` = "inherit"
AND p.`post_author` = u.`ID`
AND (
p.`post_mime_type` LIKE "image%"
OR p.`post_mime_type` LIKE "video%"
)
ORDER BY RAND()
Most straightforward solution would be like this. Simply do a join for each meta-value you need.
By using LEFT OUTER JOIN instead of INNER JOIN to join the meta-data, you will still keep users for which some of these meta-data don't exist.
SELECT DISTINCT u.ID, u.DisplayName,
fn.meta_value AS firstname, fb.meta_value AS facebook, (etc.)
FROM wp_Users u
INNER JOIN wp_posts p ON p.post_author = u.ID
LEFT OUTER JOIN wp_usermeta fn ON fn.UserId = u.ID AND fn.meta_key = 'first_name'
LEFT OUTER JOIN wp_usermeta fb ON fb.UserId = u.ID AND fb.meta_key = 'facebook'
--- (etc. for each meta column you need to join)
WHERE p.post_type = 'attachment'
AND p.post_status = 'inherit'
AND (p.post_mime_type LIKE 'image%'
OR p.post_mime_type LIKE 'video%')
I would go with something like this:
SELECT DISTINCT
`wp_users`.`ID`,
`wp_users`.`display_name`,
`wp_usermeta`.`meta_key`,
`wp_usermeta`.`meta_value`
FROM
`wp_posts`
INNER JOIN
`wp_users` ON (`wp_posts`.`post_author` = `wp_users`.`ID`)
LEFT JOIN
`wp_usermeta` ON (`wp_usermeta`.`user_id` = `wp_users`.`ID`)
WHERE
`wp_posts`.`post_type` = 'attachment'
AND `wp_posts`.`post_status` = 'inherit'
AND (`wp_posts`.`post_mime_type` LIKE 'image%'
OR `wp_posts`.`post_mime_type` LIKE 'video%')
AND `wp_usermeta`.`meta_key`
IN ('first_name', 'description', 'facebook', 'google_plus', 'twitter');
LEFT JOIN must return you posts and users even if there are no meta associated with users.
are you looking for something like this?
SELECT meta.user_id, meta.meta_key, meta.meta_value
FROM wp_usermeta meta JOIN wp_posts posts
ON posts.post_author = meta.user_id
WHERE posts.post_type = "attachment"
AND posts.post_status = "inherit"
AND posts.post_mime_type RLIKE "^(image|video)"
AND meta_key IN ('first_name', 'description','facebook','google_plus','twitter');
Keep in mind that wp_posts.post_author and wp_usermeta.user_id should be indexes, otherwise this query will be slow.
Better yet if you have a joined index on wp_usermeta: user_key (user_id, meta_key). You can add this with the following command:
ALTER TABLE wp_usermeta ADD KEY user_key(user_id, meta_key);
PS If you also need display_name from the wp_users table, then you have to join it too:
SELECT meta.user_id, users.display_name, meta.meta_key, meta.meta_value
FROM wp_usermeta meta JOIN wp_posts posts
ON posts.post_author = meta.user_id
JOIN wp_users users
ON users.id = meta.user_id
WHERE posts.post_type = "attachment"
AND posts.post_status = "inherit"
AND posts.post_mime_type RLIKE "^(image|video)"
AND meta_key IN ('first_name', 'description','facebook','google_plus','twitter');
I'm trying to write a SQL update that takes part of a string and updates with part of another string (both strings already in the database).
My query looks like this, but it doesn't work :(
UPDATE wp_postmeta SET meta_value = REPLACE(meta_value,
SELECT SUBSTRING(meta_value, 1, 23) AS meta_header
FROM wp_postmeta WHERE meta_id = 8443,
SELECT SUBSTRING(meta_value, 1, 23) AS meta_header
FROM wp_postmeta WHERE meta_id = 2037)
WHERE post_id = 8443
Any ideas for how to write it?
Thanks.
Does this work?
replace into wp_postmeta (meta_id,meta_value)
select 8443,concat(SUBSTRING(meta_value,1,23),t.end)
from wp_postmeta JOIN
(select SUBSTRING(meta_value,24) as end from wp_postmeta where meta_id=8443) as t
where meta_id=2037
Solution with group_concat:
replace into wp_postmeta (meta_id,meta_value)
select 8443,group_concat(type SEPARATOR '') from
(select mem_id,substring(type,1,23) as type from wp_postmeta where mem_id=2037
UNION
select mem_id,substring(type,24) as type from wp_postmeta where mem_id=8443) as t;
Try this by joining your tables
UPDATE wp_postmeta wp1
JOIN wp_postmeta wp2 ON (wp1.meta_id =wp2.meta_id )
JOIN wp_postmeta wp3 ON (wp1.meta_id =wp3.meta_id )
SET wp1.meta_value = REPLACE(wp1.meta_value, SUBSTRING(wp2.meta_value, 1, 23) ,SUBSTRING(wp3.meta_value, 1, 23))
WHERE wp2.meta_id = 8443 AND wp3.meta_id = 2037
AND wp1.post_id = 8443
Confusing title...my apologies.
What I've got is a table with two related rows. I need to get the value of a column in one row based on the value of a column in another row.
Given the following postmeta table:
+----------+------------+---------------------------------------------------+--------------------+
| meta_id | post_id | meta_key | meta_value |
+----------+------------+---------------------------------------------------+--------------------+
| 6917 | 661 | member_categories_0_member_categories_name | 11 |
+----------+------------+---------------------------------------------------+--------------------+
| 6918 | 661 | member_categories_0_member_categories_description | First description |
+----------+------------+---------------------------------------------------+--------------------+
| 6919 | 661 | member_categories_1_member_categories_name | 12 |
+----------+------------+---------------------------------------------------+--------------------+
| 6920 | 661 | member_categories_1_member_categories_description | Second description |
+----------+------------+---------------------------------------------------+--------------------+
I need to get the meta_value category description based on the meta_value category ID and the post_id. For instance, if my category ID is 11 and my post_id is 661, I should get "First description".
I thought about using a subquery to get the meta_key corresponding with a meta_value of '11', but I don't know how to find the description based on the counter inside 'member_categories_x_member_categories_name'.
Unfortunately, I don't have control over the name of the meta_key. I got as far as this simple query, which returns 'member_categories_0_member_categories_name'. How do I use that value to find 'First description'?
SELECT pm.meta_key
FROM postmeta pm
WHERE pm.meta_value = "11"
AND pm.post_id = 661
Here's the SQL for the table:
CREATE TABLE `postmeta` (
`meta_id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`post_id` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0',
`meta_key` VARCHAR(255) NULL DEFAULT NULL,
`meta_value` LONGTEXT NULL,
PRIMARY KEY (`meta_id`),
INDEX `post_id` (`post_id`),
INDEX `meta_key` (`meta_key`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=30814;
INSERT INTO `postmeta` (`meta_id`, `post_id`, `meta_key`, `meta_value`) VALUES (6917, 661, 'member_categories_0_member_categories_name', '11');
INSERT INTO `postmeta` (`meta_id`, `post_id`, `meta_key`, `meta_value`) VALUES (6918, 661, 'member_categories_0_member_categories_description', 'First description');
INSERT INTO `postmeta` (`meta_id`, `post_id`, `meta_key`, `meta_value`) VALUES (6919, 661, 'member_categories_1_member_categories_name', '12');
INSERT INTO `postmeta` (`meta_id`, `post_id`, `meta_key`, `meta_value`) VALUES (6920, 661, 'member_categories_1_member_categories_description', 'Second description');
This is ugly, but then, that table is so far from normalized that any answer short of scanning every row is going to be something like this:
SELECT pm2.meta_value
FROM postmeta pm1
JOIN postmeta pm2
ON pm1.post_id = pm2.post_id
AND SUBSTRING(pm1.meta_key,1,LENGTH(pm1.meta_key)-5) = SUBSTRING(pm2.meta_key,1,LENGTH(pm2.meta_key)-12)
AND pm1.meta_key like '%_name'
AND pm2.meta_key like '%_description'
WHERE pm1.meta_value = 11
AND pm1.post_id = 661
The idea is to join the table to itself, linking rows that have the same post_id, and whose meta_key is 'similar' -- it needs to be exactly the same, except that one ends with _name and one ends with _description.
I am not sure if I have fully understood what you want.
This gives the full "table", if you want to add a "where" clause you can add it at the end but for best performance, add inside the subqueries.
select
n.*, d.meta_value as meta_value_descrip
from
(
select left(meta_key, 34) as xjoin, p.*
from postmeta p where right(meta_key, 4) = 'name'
) as n
left join -- or inner join
(
select left(meta_key, 34) as xjoin, p.*
from postmeta p where right(meta_key, 11) = 'description'
) as d
on n.post_id = d.post_id and n.xjoin = d.xjoin
This's raw, but quicker than extract the number from the string,
SELECT pm2.meta_key, pm2.meta_value
FROM postmeta pm, postmeta pm2
WHERE pm.meta_value = "11"
AND pm.post_id = 661
and pm.meta_value = pm2.meta_value
AND pm.post_id = pm2.post_id
and substring(pm.meta_key,30) = substring(pm2.meta_key,30)
I apologizes, I made some mistakes, follows the correct one:
SELECT pm2.meta_value
FROM postmeta pm, postmeta pm2
WHERE pm.meta_value = "11"
AND pm.post_id = 661
and pm.meta_id <> pm2.meta_id
AND pm.post_id = pm2.post_id
and substring(pm.meta_key,19,20) = substring(pm2.meta_key,19,20)
As I told this's raw but uses (IMHO) less CPU from DB and made a better uses of indexes. Of course this is safe only for 20 digits.
of course if you have more than 2 meta_key (name and description) you should add:
AND pm2.meta_key like '%_description'
AND pm.meta_key like '%_name'
This index is unuseful (meta_key is subject of elaboration):
INDEX `meta_key` (`meta_key`)
better add this one:
INDEX `idx_post_id_meta_value` (`post_id`,`meta_value`)