UPDATE The pay query is now solved with the help provided in the replies. The answer Strawberry provided is very close. It just needed to be edited a bit as the WHERE clause has a redundancy and incorrect statement that I provided in my original query. Here is the correct, slightly revised query:
SELECT ux.user_id
, ux.meta_value pay
FROM wp_um_groups_members m
Join wp_usermeta ux
ON m.user_id1 = ux.user_id
WHERE ux.meta_key = CONCAT('_um_groups_', m.group_id,'_price')
This matches the following data for my 'Pay' field:
From up_usermeta:
umeta_id | user_id | meta_key | meta_value
===========================================================
622680 | 5989 | _um_groups_47652_price | 500
From wp_um_groups_members:
id | group_id | user_id1 | user_id2 | status | role | invites | time_stamp | date_joined
===============================================================================================================
187 | 47682 | 5989 | 3 | approved | member | 1 | 2020-02-15 10:59:08 | 2020-02-15 10:59:08
I through that in with my original query below, and everything is working.
Original Post
I'm sure I'm making this much more difficult than it needs to be. Below is the flow chart and query. When I add the 'Pay'subquery, I receive results that are not matched correctly. If I remove the "Pay" subquery, it works fine (with exception that there is no pay).
In the database, I have many entries in wp_usermeta.meta_value such as:
meta_value = _um_groups_47859_price
In that example, the number 47859 equals the value of wp_um_groups_members.group_id
So I was hoping to be able to query that exact match using something similar to: LIKE CONCAT('%', wp_um_groups_members.group_id ,'%')
That did not seem to work either.
Here is my current query:
SELECT
wp_um_groups_members.group_id AS ID, wp_posts.post_title AS Gig, Location.Location, Type.`Event Type`, wp_users.display_name AS Player, Date.Date, Pay.Pay
FROM
wp_um_groups_members
Inner Join wp_posts ON
wp_um_groups_members.group_id = wp_posts.ID
Inner Join (SELECT
wp_postmeta.post_id, wp_postmeta.meta_value AS Location
FROM
wp_postmeta
WHERE
wp_postmeta.meta_key = '_um_groups_event_location'
) Location ON
Location.post_id = wp_posts.ID
Inner Join (SELECT
wp_postmeta.post_id, wp_postmeta.meta_value AS `Event Type`
FROM
wp_postmeta
WHERE
wp_postmeta.meta_key = '_um_groups_event_type'
) Type ON
Type.post_id = wp_posts.ID
Inner Join wp_users ON
wp_users.ID = wp_um_groups_members.user_id1
Inner Join (SELECT
wp_postmeta.post_id, wp_postmeta.meta_value AS Date
FROM
wp_postmeta
WHERE
wp_postmeta.meta_key = '_um_groups_event_start'
) Date ON
Date.post_id = wp_posts.ID
Inner Join (SELECT
wp_usermeta.user_id, wp_usermeta.meta_value AS Pay
FROM
wp_um_groups_members
Inner Join wp_usermeta ON
wp_um_groups_members.user_id1 = wp_usermeta.user_id
WHERE
wp_usermeta.meta_key like '%price'
) Pay ON
Pay.user_id = wp_users.ID
WHERE
wp_um_groups_members.status = 'approved'
Is there a way to to modify that last Inner Join in order to contain something like: WHERE
wp_usermeta.meta_value = '_um_groups_47859_price' in which 47859 would is wp_um_groups_members.group_id?
SELECT ux.user_id
, ux.meta_value pay
FROM wp_um_groups_members m
Join wp_usermeta ux
ON m.user_id1 = ux.user_id
WHERE ux.meta_key LIKE '%price'
AND ux.meta_value = CONCAT('_um_groups_', m.group_id,'_price')
Incidentally, while there's no performance benefit, when working with an EAV (like wp_postmeta), I prefer this syntax...
SELECT post_id
, MAX(CASE WHEN meta_key = '_um_groups_event_location' THEN meta_value END) location
, MAX(CASE WHEN meta_key = '_um_groups_event_type' THEN meta_value END) event_type
, MAX(CASE WHEN meta_key = '_um_groups_event_start' THEN meta_value END) date
, MAX(CASE WHEN meta_key = '_um_groups_event_type' THEN meta_value END) event_type
FROM wp_postmeta
GROUP
BY post_id
(Strictly speaking, this is more akin to using LEFT JOIN than INNER JOIN, as per your example)
Related
I have a Wordpress instance showing some posts. Each post is defined in a specific language and has a property _post_year set. So we can have several posts with the same language and referring to the same year.
MySQL tables:
wp-posts
Contains all posts.
ID | post_author | post_date | ...
==================================
1 | ...
2 | ...
...
wp_term_relationships
Contains information about a language of a post (amongst other things).
object_id | term_taxonomy_id | term_order |
===========================================
1 | ...
1 | ...
2 | ...
...
wp_postmeta
Contains post meta information (like an additional property "_post_year").
meta_id | post_id | meta_key | meta_value |
===========================================
1 | 1 | ...
2 | 1 | ...
...
I once was able to load one random post per year (for all years available) like this:
SELECT DISTINCT
wp_posts.*,
postmeta.meta_value as post_meta_year
FROM (
SELECT * FROM wp_posts
JOIN wp_term_relationships as term_relationships
ON term_relationships.object_id = wp_posts.ID
AND term_relationships.term_taxonomy_id IN ({LANGUAGE_ID})
ORDER BY RAND()
) as wp_posts
JOIN wp_postmeta as postmeta
ON postmeta.post_id = wp_posts.ID
AND postmeta.meta_key = '_post_year'
AND post_status = 'publish'
GROUP BY post_meta_year DESC
ORDER BY post_meta_year DESC
Since i upgraded MySQL to version 5.7 this doesn't work anymore:
Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'wp_posts.ID' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
How can i achieve to get a random post per year sorted descendingly?
One method you can try: From a derived table with the distinct years select the year and, in a correlated subquery, a random post ID with that year using ORDER BY rand() and LIMIT 1. Join the result of that second derived table with the posts.
SELECT po1.*,
ppmo1.meta_value
FROM (SELECT pmo1.meta_value,
(SELECT pi1.id
FROM wp_posts pi1
INNER JOIN wp_postmeta pmi2
ON pmi2.post_id = pi1.id
INNER JOIN wp_term_relationships tri1
ON tri1.object_id = pi1.id
WHERE tri1.term_taxonomy_id = {LANGUAGE_ID}
AND pmi2.meta_key = '_post_year'
AND pmi2.meta_value = pmo1.meta_value
ORDER BY rand()
LIMIT 1) id
FROM (SELECT DISTINCT
pmi1.meta_value
FROM wp_postmeta pmi1
WHERE pmi1.meta_key = '_post_year') pmo1) ppmo1
INNER JOIN wp_posts po1
ON po1.id = ppmo1.id
ORDER BY ppmo1.meta_value DESC;
(Untested because schema and sample data weren't given by consumable DDL and DML.)
In MySQL 5.7, where mode ONLY_FULL_GROUP_BY is (happily) enabled by default, I would recommend a correlated subquery for filtering:
select * -- better enumerate the actual column names here
from wp_posts p
inner join wp_postmeta pm on pm.post_id = p.id
where pm.meta_key = '_post_year' and p.id = (
select pm1.post_id
from wp_post p1
inner join wp_postmeta pm1 on pm1.post_id = p1.id
where p1.status = 'publish' and pm1.meta_key = '_post_year' and pm1.meta_value = pm.meta_value
order by rand() limit 1
)
Basically the subquery selects one random post id per group of records having the same '_post_year', which is used to filter the query.
Note that with this technique there is no need to filter again in the outer query on the post status, since the subquery does it already and returns a primary key column.
I need to use some library to export product names, SKU and prices to one CSV file. This library connects using PDO and needs an SQL query.
I want to select 'name', 'SKU' and 'price' from 2 WordPress tables, namely wp_posts and wp_postmeta.
I don't know how to get data from 'meta_value' column twice for 'meta_key'='_price' and 'meta_key'='_sku', ex.
My current query:
"SELECT a.post_title, m1.meta_value, m2.meta_value FROM wp_posts a, wp_postmeta m1, wp_postmeta m2
WHERE a.post_type='product' AND m1.post_id = a.ID
AND m1.meta_key='_sku'
AND m2.meta_key='_price'"
It sounds like you could do with a join so you're relating the meta information to the right posts.
SELECT
post.post_title,
meta.meta_value
FROM wp_posts AS post
LEFT JOIN wp_postmeta AS meta
ON post.post_id = meta.post_id
WHERE post.post_type = 'product'
AND meta.meta_key IN ('_sku', '_price')
Example results:
post_title | meta_value
--------------|-----------
Cheddar | CHE001
Cheddar | 2.45
Red Leicester | CHE002
...
This assumes that the id column in wp_posts is post_id.
It's important to note that this will return up to two rows for each post, depending on whether it has a meta row for _sku and _price). If you need the data all on the same row (as you might for your export) you might need something like this instead:
SELECT
post.post_title,
metaSku.meta_value AS sku,
metaPrice.meta_value AS price
FROM wp_posts AS post
LEFT JOIN (
SELECT
*
FROM wp_postmeta
WHERE meta_key = '_sku'
) AS metaSku
ON post.post_id = metaSku.post_id
LEFT JOIN (
SELECT
*
FROM wp_postmeta
WHERE meta_key = '_price'
) AS metaPrice
ON post.post_id = metaPrice.post_id
WHERE post.post_type = 'product'
Example results:
post_title | sku | price
--------------|--------|------
Cheddar | CHE001 | 2.45
Red Leicester | CHE002 |
...
I hope this helps.
This will works for me
SELECT post.post_title, metaSku.meta_value AS sku, metaPrice.meta_value AS price FROM wp_posts AS post LEFT JOIN ( SELECT * FROM wp_postmeta WHERE meta_key = '_sku' ) AS metaSku ON post.ID = metaSku.post_id LEFT JOIN ( SELECT * FROM wp_postmeta WHERE meta_key = '_price' ) AS metaPrice ON post.ID = metaPrice.post_id WHERE post.post_type = 'product';
This takes place inside WordPress, but it's a general MySQL question.
There are two tables, one of which contains posts, the other metadata, linked by ID.
post_title | ID post_id | meta_key | meta_value
-----------+--- --------+----------+-----------
title | 1 1 | key_1 | aaa
-----------+--- --------+----------+-----------
title | 2 1 | key_2 | bbb
--------+----------+-----------
1 | mykey | 1
--------+----------+-----------
2 | key_n | ccc ddd
I'm trying to order results on some column value, which might not be set for all rows. Basically, I want to see rows with this column/value pair set first, followed by all the others. Each post might have some metadata associated with it, based on meta_key and meta_value pairs. There may be more keys for a single post and they need not include the one I want to sort by.
The problem is that using a MySQL query with a WHERE meta_key = mykey will exclude all the posts where this key doesn't exist. So what I need is a way to display a default value for all those posts, where this meta key doesn't exist.
First step: It's easy to select all rows with a certain meta_key:
SELECT
p.ID, p.post_title, p.post_type, p.post_date, m.meta_value
FROM wp_posts AS p
LEFT JOIN wp_postmeta AS m ON p.ID = m.post_id
WHERE
m.meta_key = 'mykey'
Second step: how do I select all the rows where this meta_key doesn't exist?
Here's what I mean, but this is probably a bad solution:
SELECT
p.ID, p.post_title, p.post_type, p.post_date, "some_default"
FROM wp_posts AS p
WHERE
p.ID NOT IN (
SELECT
p.ID
FROM wp_posts AS p
LEFT JOIN wp_postmeta AS m ON p.ID = m.post_id
WHERE
m.meta_key = 'mykey'
)
Third step: show combined results. This could be a UNION of both queries above.
I'm sure there must be a better sulution. What's more important, I don't know how to specify additional paramaters – e. g., first find all posts with some given meta key, or title, or category etc. and then order by said mykey as layed out above.
FINAL EDIT
If anyone's interested, here's the final solution in context. RedFilter's answer made it possible, thanks again.
SELECT p1.ID, p1.post_title, p1.post_type, p1.post_date, m1.meta_value AS meta1, meta2
FROM wp_posts AS p1
LEFT JOIN wp_postmeta AS m1 ON m1.post_id = p1.ID
LEFT JOIN wp_term_relationships AS tr0 ON tr0.object_id = p1.ID
LEFT JOIN wp_term_taxonomy AS tt0 ON tr0.term_taxonomy_id = tt0.term_taxonomy_id
LEFT JOIN wp_terms AS t0 ON tt0.term_id = t0.term_id
LEFT JOIN
(
SELECT
p.ID, IF (m.meta_value = 'on', 1, 0) AS meta2
FROM wp_posts AS p
LEFT JOIN wp_postmeta AS m
ON p.ID = m.post_id
and m.meta_key = 'mykey'
) as extra
ON extra.ID = p1.ID
WHERE 1 = 1
AND m1.meta_key = 'some-other-meta-key'
AND p1.post_type IN ('post', 'some-custom-post-type')
AND tt0.taxonomy = 'some-taxonomy'
AND t0.term_id = 'some-id'
ORDER BY meta2 DESC, meta1 ASC, p1.post_date DESC
SELECT p.ID, p.post_title, p.post_type, p.post_date,
ifnull(m.meta_value, 'default val') as meta_value
FROM wp_posts AS p
LEFT JOIN wp_postmeta AS m ON p.ID = m.post_id
and m.meta_key = 'mykey'
I'm trying to group key = value pairs that are each on its row, into one row groupped by ID.
table 1. wp_posts
keys: ID - post_title
table 2. wp_postmeta
keys: post_id, wpcf-owner, wpcf- tel, wpcf-mob, wpcf-email...
Using this query, I'm getting all info, but for each key I get separate row. What I need is an array where all is in one row groupped by wp_posts.ID
SELECT wp_posts.ID, wp_posts.post_title, wp_postmeta.post_id, wp_postmeta.meta_key, wp_postmeta.meta_value
FROM wp_posts
JOIN wp_postmeta
ON wp_posts.ID = wp_postmeta.post_id
WHERE wp_posts.post_type = 'accommodation' AND wp_postmeta.meta_key LIKE 'wpcf%'
ORDER BY hic13_posts.post_title
Result is:
ID | post_title | key value
------------------------------
1 | TITLE | wpcf-owner
1 | TITLE | wpcf-tel
1 | TITLE | wpcf-mob
1 | TITLE | wpcf-email
2 | TITLE | wpcf-owner
2 | TITLE | wpcf-tel
2 | TITLE | wpcf-mob
2 | TITLE | wpcf-email
...
What I need is:
ID | post_title | key value | key value | key value | key value
-----------------------------------------------------------------
1 | TITLE | wpcf-owner | wpcf-tel | wpcf-mob | wpcf-email
2 | TITLE | wpcf-owner | wpcf-tel | wpcf-mob | wpcf-email
If I add command to group by ID, then I get only one key from second table...
SELECT wp_posts.ID, wp_posts.post_title, wp_postmeta.post_id, wp_postmeta.meta_key, wp_postmeta.meta_value
FROM wp_posts
JOIN wp_postmeta
ON wp_posts.ID = wp_postmeta.post_id
WHERE wp_posts.post_type = 'accommodation' AND wp_postmeta.meta_key LIKE 'wpcf%'
GROUP BY hic13_posts.ID
ORDER BY hic13_posts.post_title
I get only this
ID | post_title | key value
------------------------------
1 | TITLE | wpcf-owner
2 | TITLE | wpcf-tel
Hope all this makes sense :-)
Thank you!
This type of transformation is a pivot but MySQL does not have a pivot function so you will need to use an aggregate function with a CASE expression.:
SELECT p.ID,
p.post_title,
m.post_id,
max(case when m.meta_key = 'wpcf-owner' then m.meta_value end) as owner,
max(case when m.meta_key = 'wpcf-tel' then m.meta_value end) as tel,
max(case when m.meta_key = 'wpcf-mob' then m.meta_value end) as mob,
max(case when m.meta_key = 'wpcf-email' then m.meta_value end) as email
FROM wp_posts p
JOIN wp_postmeta m
ON p.ID = m.post_id
WHERE p.post_type = 'accommodation' AND m.meta_key LIKE 'wpcf%'
GROUP BY p.ID, p.post_title, m.post_id
ORDER BY p.post_title
Edit, if you have an unknown number of meta_key items to transform, then you can use a prepared statement to generate dynamic sql:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'max(CASE WHEN m.meta_key = ''',
meta_key,
''' THEN m.meta_value else null END) AS `',
meta_key, '`'
)
) INTO #sql
FROM wp_postmeta;
SET #sql
= CONCAT('SELECT p.ID,
p.post_title,
m.post_id, ', #sql, '
FROM wp_posts p
JOIN wp_postmeta m
ON p.ID = m.post_id
WHERE p.post_type = ''accommodation''
AND m.meta_key LIKE ''wpcf%''
GROUP BY p.ID, p.post_title, m.post_id
ORDER BY p.post_title');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
What i want is:
Get posts with date greater then 2010-03-02 and with the meta_value 'something' + like '2010-'
because there are other values like 239048192304 or 293811743
$query = "SELECT DISTINCT wp_postmeta.meta_key, wp_postmeta.meta_value, wp_posts.ID, wp_posts.guid, wp_postmeta.post_id, wp_posts.post_title
FROM wp_postmeta
INNER JOIN wp_posts
ON wp_postmeta.post_id=wp_posts.ID
WHERE wp_postmeta.meta_value >='2010-03-02'
AND wp_postmeta.meta_value = 'something'
AND wp_postmeta.meta_value LIKE '2010-'
ORDER BY wp_postmeta.meta_value ASC
LIMIT 0,10";
can you help me out please? thank you!
Update2:
table wp_postmeta
post_id | meta_value
5 | 2010-12-30
5 | Berlin
3 | 2010-12-29
3 | Paris
2 | 2009-12-29
2 | Paris
14 | 12232456521
14 | Berlin
Output:
2010-12-30 Berlin ID 5
2010-12-29 Paris ID 3
Maybe you mean an OR instead of an AND?
...
WHERE wp_postmeta.meta_value >= '2010-03-02' OR
wp_postmeta.meta_value = 'something' OR
wp_postmeta.meta_value LIKE '2010-'
Unfortunately in the English language, AND and OR can be used interchangeably in certain cases:
"I always carry an umbrella for when it rains and snows."
"I always carry an umbrella for when it rains or snows."
The above wouldn't be equivalent for computers :)
A larger data set and sample answer would help clarify the question but here is my interpretation of what you are looking for. It's not elegant but if you've got the buffer space allocated it works.
SELECT DISTINCT wp_postmeta.meta_key, wp_postmeta.meta_value, wp_posts.ID, wp_posts.guid, wp_postmeta.post_id, wp_posts.post_title
FROM wp_postmeta
INNER JOIN wp_posts
ON wp_postmeta.post_id=wp_posts.ID
WHERE wp_postmeta.post_id IN (
select post_id from wp_postmeta where str_to_date(meta_value, '%Y-%m-%d') >= 2010-03-02' and post_id in (select post_id from wp_postmeta where meta_value = 'something')
);
For getting post information and their post meta values you need the following query:
SELECT DISTINCT wp_postmeta.meta_key, wp_postmeta.meta_value, wp_posts.ID,
wp_posts.guid, wp_postmeta.post_id, wp_posts.post_title
FROM wp_postmeta JOIN wp_posts
ON wp_postmeta.post_id = wp_posts.ID
WHERE wp_posts.post_date >= '2010-03-02'
AND EXISTS (SELECT 1 from wp_postmeta m1
WHERE m1.post_ised = wp_posts.ID
AND wp_postmeta.meta_value = 'something'
AND EXISTS (SELECT 1 from wp_postmeta m2
WHERE m2.post_ised = wp_posts.ID
AND wp_postmeta.meta_value LIKE '2010-%')
ORDER BY wp_postmeta.meta_value
ASC LIMIT 0, 10
Try this:
$query = "SELECT p.ID, m1.meta_value, m2.meta_value, p.post_title FROM
wp_posts p, wp_postmeta m1, wp_postmeta m2
WHERE p.post_date > '2010-03-02' AND
m1.post_id=p.ID AND m2.post_id=p.ID AND
m2.meta_value LIKE '2010-%' AND
m1.meta_value = 'something'
ORDER BY m1.meta_value, m2.meta_value
LIMIT 0,10";
No need for the distinct, since we're showing everything on one row anyway.
Those statements contradict themselves. First you are asking for it to be >= a date. So is meta_value a date? Then you say it must be equaled to 'something', so now it is a string and not a date. Finally you say have it be like 2010-, so now we are back to a string or a date, but no wild card % in the like so you are basically saying it has to be equaled to 2010- as well.
What is the value stored in meta_value?
Are you sure you do not mean to query multiple fields?
UPDATE
Based on the new information, I think this is what you want:
$query = "SELECT DISTINCT wp_postmeta.meta_key, wp_postmeta.meta_value, wp_posts.ID, wp_posts.guid, wp_postmeta.post_id, wp_posts.post_title
FROM wp_postmeta
INNER JOIN wp_posts
ON wp_postmeta.post_id=wp_posts.ID
WHERE (wp_postmeta.meta_value = 'something'
OR wp_postmeta.meta_value LIKE '2010-%')
ORDER BY wp_postmeta.meta_value ASC
LIMIT 0,10";
Hopefully that is what you were after. I removed the 2010 >= condition given that the 2010 LIKE condition will pull that same data.