Query with Foreach inside - mysql

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);
?>

Related

How do I combine 2 SELECT statements where the result

How do I combine 2 SELECT statements where the result of the first select is used in the WHERE of the second SELECT
Below is the code I am using right now:
$order_id = 7655;
$first = $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM wp_postmeta WHERE meta_key = '_ticket_order' AND meta_value = %d", $order_id ) );
if ( $first ) {
$second = $wpdb->get_var( $wpdb->prepare( "SELECT meta_value FROM wp_postmeta WHERE meta_key = '_ticket_event' AND post_id = %d", $first ) );
}
echo $second;
You could try using a join between the two queries
$second = $wpdb->get_var( $wpdb->prepare("SELECT b.meta_value
FROM wp_postmeta a
INNER JOIN wp_postmeta b ON a.post_id = b.post_id
WHERE a.meta_key = '_ticket_order' AND a.meta_value = %d", $order_id ) );

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.

SQL - How to concatenate this table?

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.

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();

Wordpress Query order by custom field missing posts

This is my current query:
query_posts(array_merge(array('tag' => $pagetag,'meta_key'=>priority,'orderby' =>meta_value, 'order' =>'ASC','paged' => get_query_var('paged'))));
My problem is that the query shows me only the post that has values for my 'meta_key' meaning that 'priority' is not NULL.
How can I improve this query so that it will still orderby my meta_key but will show all the posts that aren't NULL as well?
Thanks in advance!
The problem is that WordPress adds an INNER JOIN to the wp_postmeta table as soon as you mention meta_key in your conditions. One way around the problem is to add a filter on the order by clause, something like this:
function so_orderby_priority($original_orderby_statement) {
global $wpdb;
return "(SELECT $wpdb->postmeta.meta_value
FROM $wpdb->postmeta
WHERE $wpdb->posts.ID = $wpdb->postmeta.post_id
AND $wpdb->postmeta.meta_key = 'priority') ASC";
}
add_filter('posts_orderby', 'so_orderby_priority');
query_posts(
array(
'tag' => $pagetag,
'paged' => get_query_var('paged')
)
);
remove_filter('posts_orderby', 'so_orderby_priority');
Note MySQL sorts NULLs first - if you want them sorted last, try something like this (assuming all your priorities come before ZZZZZ alphabetically):
function so_orderby_priority($original_orderby_statement) {
global $wpdb;
return "IFNULL(
(SELECT $wpdb->postmeta.meta_value
FROM $wpdb->postmeta
WHERE $wpdb->posts.ID = $wpdb->postmeta.post_id
AND $wpdb->postmeta.meta_key = 'priority'),
'ZZZZZ') ASC";
}
Edit
Here's a bit more explanation, which assumes you understand SQL at least a bit.
Your original query_posts resulted in the following query running against the database:
SELECT wp_posts.*
FROM wp_posts
INNER JOIN wp_term_relationships ON ( wp_posts.id = wp_term_relationships.object_id )
INNER JOIN wp_postmeta ON ( wp_posts.id = wp_postmeta.post_id )
WHERE 1 = 1
AND ( wp_term_relationships.term_taxonomy_id IN ( 3 ) )
AND wp_posts.post_type = 'post'
AND ( wp_posts.post_status = 'publish'
OR wp_posts.post_status = 'private' )
AND ( wp_postmeta.meta_key = 'priority' )
GROUP BY wp_posts.id
ORDER BY wp_postmeta.meta_value ASC
LIMIT 0, 10;
That INNER JOIN wp_postmeta is what removed any posts without a priority from your results.
Removing the meta_* related conditions from your query_posts:
query_posts(
array(
'tag' => $pagetag,
'paged' => get_query_var('paged')
)
);
solved that problem, but the sort order is still wrong. The new SQL is
SELECT wp_posts.*
FROM wp_posts
INNER JOIN wp_term_relationships ON ( wp_posts.id = wp_term_relationships.object_id )
WHERE 1 = 1
AND ( wp_term_relationships.term_taxonomy_id IN ( 3 ) )
AND wp_posts.post_type = 'post'
AND ( wp_posts.post_status = 'publish'
OR wp_posts.post_status = 'private' )
GROUP BY wp_posts.id
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10;
The posts_orderby filter allows us to change the ORDER BY clause: wp_posts.post_date DESC gets replaced by what the filter returns. The final SQL becomes:
SELECT wp_posts.*
FROM wp_posts
INNER JOIN wp_term_relationships ON ( wp_posts.id = wp_term_relationships.object_id )
WHERE 1 = 1
AND ( wp_term_relationships.term_taxonomy_id IN ( 3 ) )
AND wp_posts.post_type = 'post'
AND ( wp_posts.post_status = 'publish'
OR wp_posts.post_status = 'private' )
GROUP BY wp_posts.id
ORDER BY (SELECT wp_postmeta.meta_value
FROM wp_postmeta
WHERE wp_posts.id = wp_postmeta.post_id
AND wp_postmeta.meta_key = 'priority') ASC
LIMIT 0, 10
which does what you're after.
I needed to perform a similar task on the users.php page for a custom column I added and used the following code that I modified from Hobo
add_action('pre_user_query', 'qd_users_column_orderby');
function qd_users_column_orderby($userquery){
if('my_last_login'==$userquery->query_vars['orderby']) { //check if my cusomt meta_key is the column being sorted
global $wpdb;
$userquery->query_orderby = " ORDER BY(SELECT $wpdb->usermeta.meta_value
FROM $wpdb->usermeta
WHERE $wpdb->users.ID = $wpdb->usermeta.user_id
AND $wpdb->usermeta.meta_key = 'my_last_login') ".($userquery->query_vars["order"] == "ASC" ? "asc " : "desc ")." , wp_users.user_login ".($userquery->query_vars["order"] == "ASC" ? "asc " : "desc ");
}
}
Hopefully this helps somebody in need of this information.
In an effort of being throughout, the remainder of the necessary code to complete my individual task is below.
add_filter('manage_users_columns', 'qd_add_user_login_column');
function qd_add_user_login_column($columns) {
$columns['my_last_login'] = 'Last Logged In';
return $columns;
}
add_action('manage_users_custom_column', 'qd_show_user_login_column_content', 10, 3);
function qd_show_user_login_column_content($value, $column_name, $user_id) {
$user = get_userdata( $user_id );
if ( 'my_last_login' == $column_name ){
$lastLogin = get_the_author_meta('my_last_login', $user_id);
if(!$lastLogin){
return "Never";
}else{
date_default_timezone_set(get_option('timezone_string'));
return date('m/d/y g:ia', $lastLogin);
}
}
return $value;
}
add_filter( 'manage_users_sortable_columns', 'qd_users_table_sorting' );
function qd_users_table_sorting( $columns ) {
$columns['my_last_login'] = 'my_last_login';
return $columns;
}
The Issue: Sorting by a custom field without excluding posts that don't have a value set for that custom field.
Hobo's answer explains this well. Here I'm just going to offer a simpler alternative that ended up being easier in my case (Please note this won't work correctly if pagination is needed).
I decided to do the sorting in PHP after the query is made.
The nice part is that I have better control over where the posts with null values end up (I wanted them to show up last).
$query = new WP_Query($args);
//sort by rank
function customSort($a, $b)
{
$a = get_field("sort_ranking", $a);
$b = get_field("sort_ranking", $b);
//handle nulls
$a = is_numeric($a) ? $a : 9999;
$b = is_numeric($b) ? $b : 9999;
if ($a == $b) return 0;
return ($a < $b) ? -1 : 1;
}
usort($query->posts, "customSort");
Here I have a numeric custom field called sort_ranking and I'm using it to sort ASC. Posts with a null value for this field are assigned 9999 so that they end up at the end. (Note: I'm using ACF, hence the get_field function)
Hope this helps someone!
The easiest way to do this is to insert the custom field using save_post action, so every post published will have its own meta_key with a default value.
Use a MySQL query for add post_meta to all posts has not the meta. Thats it.
If you/anyone need code help on this, just reply :)
UPDATE
As Timusan asked, add this code in your functions.php file after changing the meta-key name :
add_action('save_post', 'sidati_post_views_metakey');
function sidati_post_views_metakey ($post_id){
/*
* $post_id = is the post ID
* 'sidati_post_views' => is your metakey name (sidati is prefix always nice to add your prefix)
* 0 => the inital value
* true => (bool) add true if you want this metakkey become unique
*/
add_post_meta($post_id, 'sidati_post_views', 0, true);
}
// THIS ACTION MUST RUN ONLY ONE TIME
add_action('init', 'sidati_allposts_must_have_this');
function sidati_allposts_must_have_this(){
/* Call the WordPress DataBase class */
global $wpdb;
/* This Query will get us all the posts and pages without "sidati_post_views" metakey. */
$ids = $wpdb->get_row("SELECT ID FROM wpdb->posts WHERE post_type IN ('post', 'page') AND post_status = 'publish' AND ID NOT IN (SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'sidati_post_views')");
/* After get all posts/pages, now you need to add the meta keys (this may take a few munites if you have many posts/pages)*/
foreach ($ids as $post_id) {
add_post_meta($post_id, 'sidati_post_views', 0, true);
}
}