Optimise Custom Wordpress SQL query - php

I have a wordpress website whereby I am doing an ajax request to return a json object of property details (to use with google maps). I currently have the following query:
SELECT
p.ID AS 'id',
p.post_title AS 'title',
t.name AS 'property_type',
c.name AS 'listing_type',
pm.meta_value AS 'address',
pm2.meta_value AS 'latitude',
pm3.meta_value AS 'longitude',
pm4.meta_value AS 'price',
pm5.meta_value AS 'bedrooms',
pm6.meta_value AS 'baths',
pm7.meta_value AS 'show_date',
p.guid,
wm2.meta_value AS 'image'
FROM
wp_posts p
INNER JOIN
wp_postmeta AS pm ON pm.post_id = p.ID
INNER JOIN
wp_postmeta AS pm2 ON pm2.post_id = p.ID
INNER JOIN
wp_postmeta AS pm3 ON pm3.post_id = p.ID
INNER JOIN
wp_postmeta AS pm4 ON pm4.post_id = p.ID
INNER JOIN
wp_postmeta AS pm5 ON pm5.post_id = p.ID
INNER JOIN
wp_postmeta AS pm6 ON pm6.post_id = p.ID
INNER JOIN
wp_postmeta AS pm7 ON pm7.post_id = p.ID
LEFT JOIN
wp_term_relationships AS r ON (p.ID = r.object_id)
INNER JOIN
wp_term_taxonomy AS x ON (r.term_taxonomy_id = x.term_taxonomy_id)
INNER JOIN
wp_terms AS t ON (r.term_taxonomy_id = t.term_id)
LEFT JOIN
wp_term_relationships AS v ON (p.ID = v.object_id)
INNER JOIN
wp_term_taxonomy AS z ON (v.term_taxonomy_id = z.term_taxonomy_id)
INNER JOIN
wp_terms AS c ON (v.term_taxonomy_id = c.term_id)
LEFT JOIN
wp_postmeta wm1 ON (wm1.post_id = p.id
AND wm1.meta_value IS NOT NULL
AND wm1.meta_key = '_thumbnail_id')
LEFT JOIN
wp_postmeta wm2 ON (wm1.meta_value = wm2.post_id
AND wm2.meta_key = '_wp_attached_file'
AND wm2.meta_value IS NOT NULL)
WHERE
pm.meta_key = 'property_address'
AND pm2.meta_key = 'property_lat'
AND pm3.meta_key = 'property_lng'
AND pm4.meta_key = 'property_price'
AND pm5.meta_key = 'property_beds'
AND pm6.meta_key = 'property_baths'
AND pm7.meta_key = 'property_show_date'
AND x.taxonomy = 'property-type'
AND z.taxonomy = 'listing-type'
AND p.post_type = 'property'
AND p.post_status = 'publish'
which returns the data perfectly:
id | title | property_type | listing_type | address | latitude | longitude | price | bedrooms | baths | show_date | guid
however, as soon as I have more than one property on the website the query seems to struggle and even breaks the database (Completely lost the ability to connect to db taking the website down, having to start again with a new db). I have pinned the problem down to the taxonomy part of my query:
LEFT JOIN
wp_term_relationships AS r ON (p.ID = r.object_id)
INNER JOIN
wp_term_taxonomy AS x ON (r.term_taxonomy_id = x.term_taxonomy_id)
INNER JOIN
wp_terms AS t ON (r.term_taxonomy_id = t.term_id)
LEFT JOIN
wp_term_relationships AS v ON (p.ID = v.object_id)
INNER JOIN
wp_term_taxonomy AS z ON (v.term_taxonomy_id = z.term_taxonomy_id)
INNER JOIN
wp_terms AS c ON (v.term_taxonomy_id = c.term_id)
but I have no idea how to improve on this.
Does anyone have any ideas?

$query = "SELECT DISTINCT
id,
post_title,
guid,
pm.meta_value AS 'address',
pm2.meta_value AS 'latitude',
pm3.meta_value AS 'longitude',
pm4.meta_value AS 'price',
pm5.meta_value AS 'bedrooms',
pm6.meta_value AS 'baths',
pm7.meta_value AS 'show_date',
wm2.meta_value AS 'image',
(SELECT
GROUP_CONCAT(wp_terms.name
SEPARATOR ', ')
FROM
wp_terms
INNER JOIN
wp_term_taxonomy ON wp_terms.term_id = wp_term_taxonomy.term_id
INNER JOIN
wp_term_relationships wpr ON wpr.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id
WHERE
taxonomy = 'property-type'
AND wp_posts.ID = wpr.object_id) AS 'property_type',
(SELECT
GROUP_CONCAT(wp_terms.name
SEPARATOR ', ')
FROM
wp_terms
INNER JOIN
wp_term_taxonomy ON wp_terms.term_id = wp_term_taxonomy.term_id
INNER JOIN
wp_term_relationships wpr ON wpr.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id
WHERE
taxonomy = 'listing-type'
AND wp_posts.ID = wpr.object_id) AS 'listing_type'
FROM
wp_posts
INNER JOIN
wp_postmeta AS pm ON pm.post_id = wp_posts.ID
INNER JOIN
wp_postmeta AS pm2 ON pm2.post_id = wp_posts.ID
INNER JOIN
wp_postmeta AS pm3 ON pm3.post_id = wp_posts.ID
INNER JOIN
wp_postmeta AS pm4 ON pm4.post_id = wp_posts.ID
INNER JOIN
wp_postmeta AS pm5 ON pm5.post_id = wp_posts.ID
INNER JOIN
wp_postmeta AS pm6 ON pm6.post_id = wp_posts.ID
INNER JOIN
wp_postmeta AS pm7 ON pm7.post_id = wp_posts.ID
INNER JOIN
wp_postmeta wm1 ON (wm1.post_id = wp_posts.id
AND wm1.meta_value IS NOT NULL
AND wm1.meta_key = '_thumbnail_id')
INNER JOIN
wp_postmeta wm2 ON (wm1.meta_value = wm2.post_id
AND wm2.meta_key = '_wp_attached_file'
AND wm2.meta_value IS NOT NULL)
WHERE
post_type = 'property'
AND pm.meta_key = 'property_address'
AND pm2.meta_key = 'property_lat'
AND pm3.meta_key = 'property_lng'
AND pm4.meta_key = 'property_price'
AND pm5.meta_key = 'property_beds'
AND pm6.meta_key = 'property_baths'
AND pm7.meta_key = 'property_show_date'";

Related

Get products with specific attribute term and specific category term in Woocommerce using SQL

I wish to find products with two different criteria.
The code I used first to search one criteria is;
SELECT rel.object_id, rel.term_taxonomy_id, tt.taxonomy, tt.term_id, ts.name
FROM df1wrmw_term_taxonomy tt
INNER JOIN df1wrmw_term_relationships rel ON tt.term_taxonomy_id = rel.term_taxonomy_id
INNER JOIN df1wrmw_terms ts ON tt.term_id = ts.term_id
WHERE tt.taxonomy = "pa_1_scale"
AND ts.term_id = 400;
This returns all those products (Object_ID) with the attribute "pa_1_scale" and ts.term_id = 400. I can also do this to return all products with product_cat and ts.term_id = 397, using a different WHERE statement
WHERE tt.taxonomy = "product_cat"
AND ts.term_id = 397
UNION ALL just combines the two. How do I get SQL to select both these criteria? I know a WHERE statement combining the two criteria will not work as I think that no table row contains both values?
Any help available would be great.
You can try to use the following that will join duplicated tables with a different variable reference, allowing to combine both queries in one:
SELECT tr.object_id, tr.term_taxonomy_id, tt.taxonomy, t.term_id, t.name,
tr2.term_taxonomy_id as term_taxonomy_id2, tt2.taxonomy as taxonomy2,
t2.term_id as term_id2, t2.name as name2
FROM df1wrmw_term_relationships tr
INNER JOIN df1wrmw_term_taxonomy tt
ON tr.term_taxonomy_id = tt.term_taxonomy_id
INNER JOIN df1wrmw_terms t
ON tt.term_id = t.term_id
INNER JOIN df1wrmw_term_relationships tr2
ON tr.object_id = tr2.object_id
INNER JOIN df1wrmw_term_taxonomy tt2
ON tr2.term_taxonomy_id = tt2.term_taxonomy_id
INNER JOIN df1wrmw_terms t2
ON tt2.term_id = t2.term_id
WHERE tt.taxonomy = 'pa_1_scale'
AND t.term_id = 400
AND tt2.taxonomy = 'product_cat'
AND t2.term_id = 397
Or you can use in WordPress the class WPDB and its methods to get SQL query results in PHP:
global $wpdb;
$results = $wpdb->get_results("
SELECT tr.object_id, tr.term_taxonomy_id, tt.taxonomy, t.term_id, t.name,
tr2.term_taxonomy_id as term_taxonomy_id2, tt2.taxonomy as taxonomy2,
t2.term_id as term_id2, t2.name as name2
FROM {$wpdb->prefix}term_relationships tr
INNER JOIN {$wpdb->prefix}term_taxonomy tt
ON tr.term_taxonomy_id = tt.term_taxonomy_id
INNER JOIN {$wpdb->prefix}terms t
ON tt.term_id = t.term_id
INNER JOIN {$wpdb->prefix}term_relationships tr2
ON tr.object_id = tr2.object_id
INNER JOIN {$wpdb->prefix}term_taxonomy tt2
ON tr2.term_taxonomy_id = tt2.term_taxonomy_id
INNER JOIN {$wpdb->prefix}terms t2
ON tt2.term_id = t2.term_id
WHERE tt.taxonomy = 'pa_1_scale'
AND t.term_id = 400
AND tt2.taxonomy = 'product_cat'
AND t2.term_id = 397
");
// Display preformatted raw output
echo '<pre>' . print_pr($results, true) . '</pre>';
It should work.

Wordpress delete posts not in specific categories using wpdb query

How do I modify the following query to delete all posts with the custom post type "listings" that are NOT IN specific Wordpress categories?
Note, I must use $wpdb->query() in my particular situation.
My categories for exclusion are term ID's 21, 22, and 24.
$wpdb->query('DELETE FROM wp_posts WHERE post_type = "listings"');
UPDATE - THIS query brings me a lot closer to what I am after but throws a SQL error.
DELETE FROM wp_posts a
LEFT JOIN wp_term_relationships b ON ( a.ID = b.object_id )
LEFT JOIN wp_postmeta c ON ( a.ID = c.post_id )
LEFT JOIN wp_term_taxonomy d ON ( d.term_taxonomy_id = b.term_taxonomy_id )
LEFT JOIN wp_terms e ON ( e.term_id = d.term_id )
WHERE a.post_type = "listings"
AND e.term_id NOT IN (21,22,24);
Error:
WordPress database error You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'LEFT JOIN wp_term_relationships ON wp_posts.ID = wp_term_relationships.object_id' at line 1 for query DELETE FROM wp_posts LEFT JOIN wp_term_relationships ON wp_posts.ID = wp_term_relationships.object_id LEFT JOIN wp_postmeta ON wp_posts.ID = wp_postmeta.post_id LEFT JOIN wp_term_taxonomy ON wp_term_taxonomy.term_taxonomy_id = wp_term_relationships.term_taxonomy_id LEFT JOIN wp_terms ON wp_terms.term_id = wp_term_taxonomy.term_id WHERE wp_posts.post_type = "listings" AND wp_terms.term_id NOT IN (21,22,24)
$wpdb->query('DELETE wp_posts FROM wp_posts
INNER JOIN wp_term_relationships ON wp_posts.id = wp_term_relationships.object_id
INNER JOIN wp_term_taxonomy ON wp_term_relationships.object_id = wp_term_taxonomy.term_taxonomy_id
WHERE wp_posts.post_type = "listings" AND wp_term_taxonomy.taxonomy NOT IN (taxonomy_1_slug, taxonomy_2_slug, etc.)');
Using category id delete post
delete a,b,c,d
FROM esx_posts a
LEFT JOIN esx_term_relationships b ON ( a.ID = b.object_id )
LEFT JOIN esx_postmeta c ON ( a.ID = c.post_id )
LEFT JOIN esx_term_taxonomy d ON ( d.term_taxonomy_id = b.term_taxonomy_id )
LEFT JOIN esx_terms e ON ( e.term_id = d.term_id )
WHERE e.term_id = 48

Wordpress : wpdb prepare with conditional

I'm trying to use sql->prepare with many strings concatenation.
In my code below, I try to call a function which will return me an array of get_post from $wpdb->prepare.
function test($array){
$sqlprep = "SELECT SQL_CALC_FOUND_ROWS p.ID
FROM {$wpdb->prefix}posts p
LEFT JOIN {$wpdb->prefix}postmeta m ON m.post_id = p.ID
LEFT JOIN {$wpdb->prefix}term_relationships r ON (p.ID = r.object_id)
LEFT JOIN {$wpdb->prefix}term_relationships r1 ON (p.ID = r1.object_id)
LEFT JOIN {$wpdb->prefix}term_relationships r2 ON (p.ID = r2.object_id)
LEFT JOIN {$wpdb->prefix}term_taxonomy tt ON tt.term_taxonomy_id = r.term_taxonomy_id
LEFT JOIN {$wpdb->prefix}terms t ON t.term_id = tt.term_id AND t.term_id = r.term_taxonomy_id
WHERE 1=1";
if(isset($array['post_type']) && !empty($array['post_type']){
$sqlprep .= " AND p.post_type = '".$array['post_type']."'";
}
if(isset($array['post_type']) && !empty($array['post_type']){
$sqlprep .= " AND p.post_type = '".$array['post_status']."'";
}
$sql = $wpdb->prepare($sqlprep);
$ids = $wpdb->get_col($sql);
return array_map('get_post', $ids)
}
and the value is an array which is :
$args=array(
'post_type' => 'post',
'post_status' => 'published',
);
When I try to print_r($sql) to get the query, it always just stop till WHERE 1=1.
Can string concatenation be used for wpdb prepare?
Pay attention to the use of the properties in the $wpdb object and the use of the prepare method, furthermore there is an issue with the column name for the post_status:
function test($array){
$sql = "SELECT SQL_CALC_FOUND_ROWS p.ID
FROM {$wpdb->posts} p
LEFT JOIN {$wpdb->postmeta} m ON m.post_id = p.ID
LEFT JOIN {$wpdb->term_relationships} r ON (p.ID = r.object_id)
LEFT JOIN {$wpdb->term_relationships} r1 ON (p.ID = r1.object_id)
LEFT JOIN {$wpdb->term_relationships} r2 ON (p.ID = r2.object_id)
LEFT JOIN {$wpdb->term_taxonomy} tt ON tt.term_taxonomy_id = r.term_taxonomy_id
LEFT JOIN {$wpdb->terms} t ON t.term_id = tt.term_id AND t.term_id = r.term_taxonomy_id
WHERE 1=1";
if(isset($array['post_type']) && !empty($array['post_type']){
$sql .= $wpdb->prepare(" AND p.post_type = %s", $array['post_type']);
}
if(isset($array['post_type']) && !empty($array['post_type']){
$sql .= $wpdb->prepare(" AND p.post_status = %s", $array['post_status']);
}
$ids = $wpdb->get_col($sql);
return array_map('get_post', $ids)
}

Retrieve Wordpress post with featured image/thumbnail in one sql query

here's my code :
SELECT p.ID, p.post_title,
MAX(IF(pm.meta_key = 'featured_image', pm.meta_value, NULL)) AS event_imgID
FROM wpgp_posts AS p
LEFT JOIN wpgp_postmeta AS pm on pm.post_id = p.ID
WHERE p.post_type = 'product' and p.post_status = 'publish'
GROUP BY p.ID
event_imgID retrieve the ID from postmeta but the img url is stored in the wpgp_posts in the guid column
How to achieve this ?
You can join in the table to get more information about the image:
SELECT . . .
FROM (SELECT p.ID, p.post_title,
MAX(CASE WHEN pm.meta_key = 'featured_image' THEN pm.meta_value END) AS event_imgID
FROM wpgp_posts p LEFT JOIN
wpgp_postmeta pm
ON pm.post_id = p.ID
WHERE p.post_type = 'product' and p.post_status = 'publish'
GROUP BY p.ID
) pi LEFT JOIN
wpgp_posts wp
ON pi.event_imgID = wp.guid
I finally found the way to do it. I had to search thumbnail_id instead of guid :
SELECT
p1.ID, p1.post_title,
MAX(IF(pm.meta_key = 'listing_event_date', pm.meta_value, NULL)) AS event_date,
wm2.meta_value as event_img
FROM
wpgp_posts p1
LEFT JOIN wpgp_postmeta AS pm on (pm.post_id = p1.ID)
LEFT JOIN
wpgp_postmeta wm1
ON (
wm1.post_id = p1.id
AND wm1.meta_value IS NOT NULL
AND wm1.meta_key = '_thumbnail_id'
)
LEFT JOIN
wpgp_postmeta wm2
ON (
wm1.meta_value = wm2.post_id
AND wm2.meta_key = '_wp_attached_file'
AND wm2.meta_value IS NOT NULL
)
WHERE
p1.post_status='publish'
AND p1.post_type='product'
GROUP BY p1.ID, wm2.meta_id
ORDER BY
event_date ASC
Thanks

SQL string gives me all posts with a category but not those without

I'm running a Wordpress site, and I'm making a widget that gives me all posts that are the most read in the last 8 days.
When I run this SQL string I get all the posts with a category, but when a post isn't in a category, I don't get it.
How do I get all the posts that ARE in a category, AND all the posts that AREN'T in a category?
My sql string
$sql ="SELECT wp_posts.post_date, wp_posts.post_title, wp_posts.post_name, wp_posts.post_content, wp_terms.name, wp_terms.slug, wp_postmeta.meta_value FROM wp_posts
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)
INNER JOIN wp_postmeta ON (wp_postmeta.post_id = wp_posts.ID)
WHERE (wp_term_taxonomy.taxonomy = 'category'
AND wp_postmeta.meta_key = 'readCount'
AND wp_posts.post_type = 'post'
AND DATE(wp_posts.post_date) >= '$eight_days_ago'
AND wp_posts.post_status = 'publish')
GROUP BY wp_posts.ID
ORDER BY wp_postmeta.meta_value DESC
LIMIT 5";
I think this will work. You need to change the joins to left outer joins and then check for NULL in the where clause:
SELECT wp_posts.post_date, wp_posts.post_title, wp_posts.post_name, wp_posts.post_content, wp_terms.name, wp_terms.slug, wp_postmeta.meta_value FROM wp_posts
LEFT JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
LEFT JOIN wp_term_taxonomy ON (wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id)
LEFT JOIN wp_terms ON (wp_term_taxonomy.term_id = wp_terms.term_id)
LEFT JOIN wp_postmeta ON (wp_postmeta.post_id = wp_posts.ID)
WHERE ((wp_term_taxonomy.taxonomy = 'category') or (wp_term_taxonomy.taxonomy is NULL))
AND wp_postmeta.meta_key = 'readCount'
AND wp_posts.post_type = 'post'
AND DATE(wp_posts.post_date) >= '$eight_days_ago'
AND wp_posts.post_status = 'publish')
GROUP BY wp_posts.ID
ORDER BY wp_postmeta.meta_value DESC
LIMIT 5
Have the database, whatever it is, check for null in the category column:
SELECT wp_posts.post_date, wp_posts.post_title, wp_posts.post_name, wp_posts.post_content, wp_terms.name, wp_terms.slug, wp_postmeta.meta_value FROM wp_posts
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)
INNER JOIN wp_postmeta ON (wp_postmeta.post_id = wp_posts.ID)
WHERE ((wp_term_taxonomy.taxonomy = 'category' OR wp_term_taxonomy.taxonomy IS NULL)
AND wp_postmeta.meta_key = 'readCount'
AND wp_posts.post_type = 'post'
AND DATE(wp_posts.post_date) >= '$eight_days_ago'
AND wp_posts.post_status = 'publish')
GROUP BY wp_posts.ID
ORDER BY wp_postmeta.meta_value DESC
LIMIT 5

Categories