SQL - How to concatenate this table? - mysql

By running this SELECT query:
SELECT wp_posts.ID, wp_postmeta.meta_key, wp_postmeta.meta_value
FROM wp_posts
INNER JOIN wp_postmeta
ON wp_posts.ID = wp_postmeta.post_id
WHERE wp_posts.post_status = 'publish'
AND wp_posts.post_type = 'my_post_type'
AND wp_posts.post_date < NOW()
AND wp_postmeta.meta_key = 'wpcf-lat'
OR wp_postmeta.meta_key = 'wpcf-long'
I get table like this:
id meta_key meta_value
------------------------------
1270 wpcf-lat 12.6589
1270 wpcf-long 78.7425
1658 wpcf-lat 22.3654
1658 wpcf-long 65.2985
But I need result table to be like this
id wpcf-lat wpcf-long
------------------------------
1270 12.6589 78.7425
1658 22.3654 65.2985
How can I accomplish that?

For a known set of meta_key you can use the following query
select
wp.ID,
max(
case when pm.meta_key = 'wpcf-lat' then pm.meta_value end
) as `meta_value`,
max(
case when pm.meta_key = 'wpcf-long' then pm.meta_value end
) as `wpcf-long`
from wp_posts wp
join wp_postmeta pm on pm.post_id = wp.ID
group by wp.ID ;

A simple while or foreach in your PHP code is the easiest way to put the data in the format you need:
$query = '...';
$resultset = $DB->query($query);
$list = array();
while ($row = $resultset->fetchArray()) {
// Check if the entry having this ID was created before
$id = $row['id'];
if (! isset($list[$id]) {
// Create a new entry
$list[$id] = array(
'id' => $id,
'wpcf-lat' => NULL,
'wpcf-long' => NULL,
);
}
// Update the corresponding property
$key = $row['meta_key'];
$list[$id][$key] = $row['meta_value'];
}

As koushik veldanda said, you can need to pivot the table.
Something like:
SELECT wp_posts.ID,
CASE WHEN wp_postmeta.meta_key = 'wpcf-lat' THEN wp_postmeta.meta_value END AS wpcf-lat,
CASE WHEN wp_postmeta.meta_key = 'wpcf-long' THEN wp_postmeta.meta_value END AS wpcf-long
FROM wp_posts
INNER JOIN wp_postmeta
ON wp_posts.ID = wp_postmeta.post_id
WHERE wp_posts.post_status = 'publish'
AND wp_posts.post_type = 'my_post_type'
AND wp_posts.post_date < NOW()
AND wp_postmeta.meta_key = 'wpcf-lat'
OR wp_postmeta.meta_key = 'wpcf-long'
GROUP BY wp_posts.ID
I haven't tested this but it should be quite close.

Related

Mysql OR conditions and sort by relevance

I'm doing a query with a dynamic amount of OR conditions depending on the amount of terms retrieved by $_GET[].
if(isset($_GET['terms']))
{
$concat_search = 'AND wp_posts.ID = wp_term_relationships.object_id';
$query_concat_strings .= 'AND (';
$no_commas = rtrim($_GET['terms'], ", \t\n");
$terms = explode(',', $no_commas);
$total_terms = count($terms);
for($x = 0; $x < $total_terms; $x++)
{
$term_id = term_exists($terms[$x], 'especialidad');
if ($term_id !== 0 && $term_id !== null)
{
if($x == 0)
{
$query_concat_strings .= ' wp_term_relationships.term_taxonomy_id ='.$term_id[term_id];
}
else
{
$query_concat_strings .= ' OR wp_term_relationships.term_taxonomy_id ='.$term_id[term_id];
}
}
}
$query_concat_strings .= ')';
}
$querystr = "
SELECT $wpdb->posts.*
FROM $wpdb->posts, $wpdb->postmeta, $wpdb->term_relationships
WHERE $wpdb->posts.ID = $wpdb->postmeta.post_id
".$concat_search."
AND $wpdb->postmeta.meta_key = 'wpcf-fin-oferta'
AND $wpdb->postmeta.meta_value > $currentdate
AND $wpdb->posts.post_status = 'publish'
AND $wpdb->posts.post_type = 'oferta'".$query_concat_strings."
ORDER BY $wpdb->posts.ID
LIMIT $start, $posts_per_page
";
//The query echoed: SELECT wp_posts.* FROM wp_posts, wp_postmeta, wp_term_relationships WHERE wp_posts.ID = wp_postmeta.post_id AND wp_posts.ID = wp_term_relationships.object_id AND wp_postmeta.meta_key = 'wpcf-fin-oferta' AND wp_postmeta.meta_value > 1456700400 AND wp_posts.post_status = 'publish' AND wp_posts.post_type = 'oferta' AND ( wp_term_relationships.term_taxonomy_id =50 OR wp_term_relationships.term_taxonomy_id =7)
This will return all values matching wp_term_relationships.term_taxonomy_id =50 OR wp_term_relationships.term_taxonomy_id =7, but I would like to order the results giving priority to those that match not only one but more conditions, being first those witch matches more conditions than others.
Have tried this:
SELECT wp_posts.*, SUM(IF(wp_term_relationships.term_taxonomy_id ='11',1,NULL)) AS cond_matched, SUM(IF(wp_term_relationships.term_taxonomy_id ='50',1,NULL)) AS cond_matched, SUM(IF(wp_term_relationships.term_taxonomy_id ='7',1,NULL)) AS cond_matched FROM wp_posts, wp_postmeta, wp_term_relationships WHERE wp_posts.ID = wp_postmeta.post_id AND wp_posts.ID = wp_term_relationships.object_id AND wp_postmeta.meta_key = 'wpcf-fin-oferta' AND wp_postmeta.meta_value > 1456786800 AND wp_posts.post_status = 'publish' AND wp_posts.post_type = 'oferta' GROUP BY wp_posts.ID ORDER BY cond_matched;
But the results appear ordered completely randomly.
The expected result should be:
If I search for 1, 2, 3
Result 1: The one that contains all 3 values: 1,2,3,4,5,6,7
Result 2: Those having at least 2: 1,3,7,9
Result 3: those having only 1: 3,11,90,12
The only thing I needed was adding:
ORDER BY COUNT(*) DESC
At the end of the query. Obviously there was some missunderstanding, from my side, about the way Mysql works.

Wordpress SQL query to fetch the meta_key names in a custom post type

Using WP dataTables plugin and my MySQL sucks so please bear with me.
I'm trying to fetch multiple associated meta_keys (custom field names) that are allocated to a custom post type in WordPress, and display the fields as the table header with the custom field values in the rows underneath the header.
My SQL below kinda works. While it pulls in a colum called 'meta_key' and 'meta_value', it has the actual names of the key as it's data. I want the meta_key value itself as the list returned.
SELECT * FROM wp_posts INNER JOIN wp_postmeta
ON wp_posts.ID = wp_postmeta.post_id
WHERE 1=1 AND wp_posts.post_type = 'my_custom_post'
AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')
AND (wp_postmeta.meta_key = 'call_date' OR wp_postmeta.meta_key = 'call_time' OR wp_postmeta.meta_key = 'caller_number' OR wp_postmeta.meta_key = 'call_duration' OR wp_postmeta.meta_key = 'call_status' )
ORDER BY wp_posts.menu_order, wp_posts.post_date DESC LIMIT
So this creates a table with the header as 'meta_key' but I want the custom field name itself as the header. Hope this makes sense.
PS I've also tried
SELECT caller_number, call_duration, call_status, call_date, call_time
FROM wp_posts
WHERE post_type = 'my_custom_post'
AND post_status = 'publish'
FROM wp_postmeta
WHERE post_id = post_id
But it shows no data...
Seriously, any help here would be awesome.
for specific keys
get_post_meta($postid, '_metakey', single);
for all keys
get_post_meta($postid);
so you can do a foreach loop if you return just the custom posts in your sql query. But for posts, wp_query does a good job of returning posts rather than custom mysql queried.
See what is all returning...
global $wpdb;
global $post;
$cpt = "my_custom_post";
$sql = "SELECT * FROM {$wpdb->prefix}posts INNER JOIN {$wpdb->prefix}postmeta
ON {$wpdb->prefix}posts.ID = {$wpdb->prefix}postmeta.post_id
WHERE 1=1 AND {$wpdb->prefix}posts.post_type = '$cpt'
AND ({$wpdb->prefix}posts.post_status = 'publish' OR {$wpdb->prefix}posts.post_status = 'private')
AND ({$wpdb->prefix}postmeta.meta_key = 'call_date' OR {$wpdb->prefix}postmeta.meta_key = 'call_time' OR {$wpdb->prefix}postmeta.meta_key = 'caller_number' OR {$wpdb->prefix}postmeta.meta_key = 'call_duration' OR {$wpdb->prefix}postmeta.meta_key = 'call_status' )
ORDER BY {$wpdb->prefix}posts.menu_order, {$wpdb->prefix}posts.post_date DESC";
$data = $wpdb->get_results($sql);
echo "<pre>";
var_dump($data);
echo "</pre>";
die();

MySQL Update a table on these arrays?

I have a query like so:
SELECT DISTINCT(wp.`ID`), wps.sku AS sku
FROM `wp_posts` AS wp
INNER JOIN `wp_postmeta` AS wpm ON (wpm.`post_id` = wp.`ID` AND wpm.`meta_key` = '_sku' AND wpm.`meta_value` = '')
INNER JOIN `wp_product_skus` AS wps ON (wps.`id_product` = wpm.`post_id`)
WHERE wp.`post_parent` = 0 AND wp.`post_type` = 'product' AND wp.`ID` NOT IN (SELECT post_parent FROM wp_posts WHERE post_parent != 0 AND post_type = 'product_variation')
It returns output like so:
I need to perform an UPDATE on all IDs that match in the wp_postmeta table with the post_id = ID and update the meta_value of sku with the value of the sku from the above pic.
How can I do this all from within a MySQL Query?
You want to perform a UPDATE with JOIN to other tables as needed like
UPDATE `wp_posts` wp
INNER JOIN `wp_postmeta` wpm ON wpm.`post_id` = wp.`ID`
AND wpm.`meta_key` = '_sku'
AND wpm.`meta_value` = ''
INNER JOIN `wp_product_skus` wps ON wps.`id_product` = wpm.`post_id`
WHERE wp.`post_parent` = 0
AND wp.`post_type` = 'product'
AND wp.`ID` NOT IN (
SELECT post_parent FROM wp_posts
WHERE post_parent != 0
AND post_type = 'product_variation'
)
SET wp.sku = wps.sku
(OR) By directly joining with your SELECT result set like below
UPDATE `wp_posts` wp
JOIN
(
SELECT DISTINCT wp.`ID`,
wps.sku AS sku
FROM `wp_posts` wp
INNER JOIN `wp_postmeta` AS wpm ON wpm.`post_id` = wp.`ID`
AND wpm.`meta_key` = '_sku'
AND wpm.`meta_value` = ''
INNER JOIN `wp_product_skus` wps ON wps.`id_product` = wpm.`post_id`
WHERE wp.`post_parent` = 0
AND wp.`post_type` = 'product'
AND wp.`ID` NOT IN (
SELECT post_parent FROM wp_posts
WHERE post_parent != 0
AND post_type = 'product_variation')
) TAB ON wp.ID = TAB.ID
SET wp.sku = TAB.sku

Advanced MySQL query in Wordpress with JOIN

I am new to MySQL and I am trying to write a quite advanced query. But hey! Learning by doing & Stackoverflow!
I could probably get all data in a less advanced query/queries and sort the data using PHP. But I figure it could be done directly in the query.
Below is my code. Please ask question if you don't understand and I will try to explain better. And please help me correct the code if you find any errors.
The code will be used to display different fields on my wordpress pages. Different fildes will have different categories eg. sidebar-blog, sidebar-page, highlight-blog, highlight-page. It will work almost like regular posts.
Here is the database structure of Wordpress:
http://codex.wordpress.org/images/9/9e/WP3.0-ERD.png
http://codex.wordpress.org/Database_Description
Questions:
How am I supose to join the tables: wp_posts, wp_term_relationships, wp_term_taxonomy, wp_terms AND wp_postmeta?
Is it good practice to make advanced queries or should you use PHP to handle if/else functions?
<?php
$id = $post->ID; // Gets the ID of current page
$query = "
SELECT wp_posts.post_content, wp_posts.ID, wp_terms.slug # Data from two different tables
FROM wp_posts
# Cant figure out how to join the tables
INNER JOIN wp_postmeta
ON wp_posts.ID = wp_postmeta.post_id
INNER JOIN wp_term_relationships
ON wp_posts.ID = wp_term_relationships.object_id
INNER JOIN wp_term_taxonomy
ON wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id
INNER JOIN wp_terms
ON wp_term_taxonomy.term_id = wp_terms.term_id
WHERE
wp_posts.post_type = 'my-own-post-type' # Only get specific post-type
AND
wp_posts.post_status = 'publish' # Only get published posts
AND
# START - Only get categories specified here
(
wp_terms.slug = 'category-1'
OR
wp_terms.slug = 'category-2'
OR
wp_terms.slug = 'category-3'
OR
wp_terms.slug = 'category-4'
OR
wp_terms.slug = 'category-5'
)
# END - Only get categories specified here
AND
# I want to be able to include or exclude specific pages, even if category is the right one
# Exlude = Don't get data if current page ID is found (ID of current page will be checked in meta_value using %,$id,%)
# Include means: If include value is specifyed, then get data ONLY if ID is found (ID of current page will be checked in meta_value using %,$id,%)
# If not exclude and include are set, it should get the data
# START - Include exclude
(
# exclude is set, so check if page id match anywhere. If it IS found it it should not get the data, otherwise YES
wp_postmeta.meta_key = 'exclude' AND wp_postmeta.meta_value <> '%,$id,%'
OR
# include is set, so check if page id match anywhere. If it IS NOT found it it should not get the data, otherwise YES
wp_postmeta.meta_key = 'include' AND wp_postmeta.meta_value = '%,$id,%'
OR
# If exclude and include aren't set it should get the data
wp_postmeta.meta_key <> 'exkludera' AND wp_postmeta.meta_key <> 'include'
)
# END - Include exclude
";
$result = mysql_query($query);
while($row = mysql_fetch_array($result))
{
// collect all fields in category 1 (eg. sidebar)
if($row['slug'] == 'category-1'){
$all_fields_from_category_1 = $all_fields_from_category_1.'
<div id="field-sidebar">
'.$row['post_content'].'
</div>
';}
// collect all fields in category 1 (eg. highlight)
if($row['slug'] == 'category-2'){
$all_fields_from_category_2 = $all_fields_from_category_2.'
<div id="field-highlight">
'.$row['post_content'].'
</div>
';}
} // end while
// sidebar
echo '<div id="container-sidebar">'.
$all_fields_from_category_1.
'</div>';
// highlight
echo '<div id="container-highlight">'.
$all_fields_from_category_1.
'</div>';
?>
New version, new question:
SELECT DISTINCT wp_posts.post_content, wp_posts.ID, wp_terms.slug
FROM wp_posts
JOIN wp_postmeta ON wp_posts.ID = wp_postmeta.post_id
JOIN wp_term_relationships ON wp_posts.ID = wp_term_relationships.object_id
JOIN wp_term_taxonomy ON wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id
JOIN wp_terms ON wp_term_taxonomy.term_id = wp_terms.term_id
WHERE wp_posts.post_type = 'my-own-fields'
AND wp_posts.post_status = 'publish'
AND wp_terms.slug IN
('field1', 'field2', 'field3', 'field4', 'field5', 'field6')
AND
(
wp_postmeta.meta_key = 'exlude' AND wp_postmeta.meta_value <> '$id'
OR wp_postmeta.meta_key = 'include' AND wp_postmeta.meta_value = '$id'
OR wp_postmeta.meta_key <> 'exlude' AND wp_postmeta.meta_value <> 'include' #LAST ROW
)
The "#LAST ROW" gives me back data even if exclude or include is set. Thats becauce it matches two other rows in the table wp_postmeta.
Example of table wp_postmeta:
meta_id post_id meta_key meta_value
1 30 include 18
2 30 _edit_lock 1322225789:1
3 30 _edit_last 1
If meta_key inklude or exclude cant be found for the post_id I want to return the data...
Example senario when I want to return the data:
meta_id post_id meta_key meta_value
2 30 _edit_lock 1322225789:1
3 30 _edit_last 1
Any idea of how I can solve this?
A statement that say: If no rows with exclude or include was found for current id. Return the data.
More examples:
The post_id is 30.
If senario is:
meta_id post_id meta_key meta_value
1 30 include 18
2 30 _edit_lock 1322225789:1
3 30 _edit_last 1
Then I want to get back wp_posts.post_content, wp_posts.ID and wp_terms.slug AS LONG AS $id IS 18.
If senario is:
meta_id post_id meta_key meta_value
1 30 exclude 18
2 30 _edit_lock 1322225789:1
3 30 _edit_last 1
Then I want to get back wp_posts.post_content, wp_posts.ID and wp_terms.slug AS LONG AS $id IS NOT 18.
If senario is:
meta_id post_id meta_key meta_value
2 30 _edit_lock 1322225789:1
3 30 _edit_last 1
Then I want to get back wp_posts.post_content, wp_posts.ID and wp_terms.slug.
Your joins are done correctly. When removing your comments and extra lines, you will see that the query is not really that complicated. The only real change I made was to change your list of OR for wp_terms.slug to an IN statement. This was only done to reduce code duplication, clean up the look, not to change functionality.
SELECT p.post_content, p.ID, t.slug
FROM wp_posts AS p
INNER JOIN wp_postmeta AS pm ON p.ID = pm.post_id
INNER JOIN wp_term_relationships AS tr ON p.ID = tr.object_id
INNER JOIN wp_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
INNER JOIN wp_terms AS t ON tt.term_id = t.term_id
WHERE p.post_type = 'my-own-post-type'
AND p.post_status = 'publish'
AND t.slug IN
('category-1', 'category-2', 'category-3', 'category-4', 'category-5')
AND
(
( pm.meta_key = 'exclude' AND pm.meta_value <> '$id' )
OR ( pm.meta_key = 'include' AND pm.meta_value = '$id' )
OR ( pm.meta_key <> 'exclude' AND pm.meta_value <> 'include' )
)

Query with Foreach inside

I would like Wordpress query to have a foreach in it.
$aid=0;
foreach ($QA as $key => $value) {
AND wp_postmeta.post_id IN (
SELECT wp_postmeta.post_id
FROM wp_postmeta, wp_posts
WHERE wp_posts.ID = wp_postmeta.post_id
AND wp_posts.post_status = 'publish'
AND wp_posts.post_type = 'post'
AND wp_postmeta.meta_key = '$key'
AND wp_postmeta.meta_value = '$value'
)
$aid++;
}
But it's not working that way, so I'm asking for help -
How I can put foreach in sql query?
Full example of code below.
<?php
function query_products($QA) {
global $wpdb;
global $post;
global $pageposts;
$querystr = "
SELECT wp_posts.*, wp_postmeta.*
FROM wp_postmeta, wp_posts
WHERE wp_posts.ID = wp_postmeta.post_id
AND wp_posts.post_status = 'publish'
AND wp_posts.post_type = 'post'
AND wp_postmeta.meta_key = 'country'
AND wp_postmeta.meta_value = 'Denmark'
$aid=0;
foreach ($QA as $key => $value) {
AND wp_postmeta.post_id IN (
SELECT wp_postmeta.post_id
FROM wp_postmeta, wp_posts
WHERE wp_posts.ID = wp_postmeta.post_id
AND wp_posts.post_status = 'publish'
AND wp_posts.post_type = 'post'
AND wp_postmeta.meta_key = '$key'
AND wp_postmeta.meta_value = '$value'
)
$aid++;
}
ORDER BY wp_postmeta.meta_value ASC
";
$pageposts = $wpdb->get_results($querystr, OBJECT);
}
$QA = array (
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
);
query_products($QA);
?>
$querystr = "
SELECT wp_posts.*, wp_postmeta.*
FROM wp_postmeta, wp_posts
WHERE wp_posts.ID = wp_postmeta.post_id
AND wp_posts.post_status = 'publish'
AND wp_posts.post_type = 'post'
AND wp_postmeta.meta_key = 'country'
AND wp_postmeta.meta_value = 'Denmark'
";
$aid=0;
foreach ($QA as $key => $value) {
$querystr .= "
AND wp_postmeta.post_id IN (
SELECT wp_postmeta.post_id
FROM wp_postmeta, wp_posts
WHERE wp_posts.ID = wp_postmeta.post_id
AND wp_posts.post_status = 'publish'
AND wp_posts.post_type = 'post'
AND wp_postmeta.meta_key = '$key'
AND wp_postmeta.meta_value = '$value'";
)
$aid++;
}
$querystr .=" ORDER BY wp_postmeta.meta_value ASC";
Without knowing what the query is supposed to accomplish, I can only suggest something like this:
<?php
$querystr = "
SELECT wp_posts.*, wp_postmeta.*
FROM wp_postmeta, wp_posts
WHERE wp_posts.ID = wp_postmeta.post_id
AND wp_posts.post_status = 'publish'
AND wp_posts.post_type = 'post'
AND wp_postmeta.meta_key = 'country'
AND wp_postmeta.meta_value = 'Denmark'
AND wp_postmeta.post_id IN (
SELECT wp_postmeta.post_id
FROM wp_postmeta, wp_posts
WHERE wp_posts.ID = wp_postmeta.post_id
AND wp_posts.post_status = 'publish'
AND wp_posts.post_type = 'post'
";
if (count($QA) >= 1) {
$querystr .= " AND ( ";
foreach($QA as $key => $value) {
$querystr .= "(wp_postmeta.meta_key = '{$key}' AND wp_postmeta.meta_value = '{$value}') OR ";
}
// get rid of the last "OR" in the string
$querystr = substr($querystr,0,strrpos($querystr,"OR"));
$querystr .= " ) ";
}
$querystr .= " ORDER BY wp_postmeta.meta_value ASC ";
?>
Note I used "OR" above because you'll never get any results when checking a single field for multiple values using an "AND" clause.
You need to pass all the keys in as an array or issue the full query iteratively (including the select). I would not recommend the latter, because performance will be poor (excessive database round trips are bad). So, I'd do something like this as long as $QA is not too huge. I believe it to be a little bit simpler than the full subquery originally submitted by using an EXISTS clause, but this may be more a matter of taste than one truly being better than the other.
<?php
function query_products($QA) {
global $wpdb;
global $post;
global $pageposts;
$querystr = "
SELECT wp_posts.*, wp_postmeta_a.*
FROM wp_postmeta AS wp_postmeta_a, wp_posts, wp_postmeta AS wp_postmeta_b
WHERE wp_posts.ID = wp_postmeta_a.post_id
AND wp_posts.post_status = 'publish'
AND wp_posts.post_type = 'post'
AND wp_postmeta_a.meta_key = 'country'
AND wp_postmeta_a.meta_value = 'Denmark'
";
$aid=0;
foreach ($QA as $key => $value) {
$querystr .= "
AND EXISTS (SELECT 1
FROM wp_postmeta AS wp_postmeta_b
WHERE wp_posts.ID = wp_postmeta_b.post_id
AND wp_postmeta_b.meta_key = '$key'
AND wp_postmeta_b.meta_value = '$value')
";
$aid++;
}
$querystr .= " ORDER BY wp_postmeta_a.meta_value ASC ";
$pageposts = $wpdb->get_results($querystr, OBJECT);
}
$QA = array (
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
);
query_products($QA);
?>