Okay, I've been at this logic going on weeks now, and I've yet to produce anything that will work.
I'm using Wordpress, so this is saved as a string (which has PHP in it) before running through a query
If you look at http://www.libertyguide.com/jobs you can see a filter. What I want to do is use AND filtering between those three 'categories', but use OR filtering in between.
For example:
If I select Academia,Law,Policy,Full-time,Part-time,Early-Career, I expect to get a post that matches this filtering logic:
Post has (academia OR law OR policy) AND (full-time OR part-time) AND (early-career).
So essentially I want to grab posts that match academia, law, or policy, and then filter out using full-time or part-time, and finally finish with checking if it has early-career.
Here's an example of the query that I run using the above example:
SELECT * FROM $wpdb->posts
LEFT JOIN $wpdb->postmeta ON($wpdb->posts.ID = $wpdb->postmeta.post_id)
LEFT JOIN $wpdb->term_relationships ON($wpdb->posts.ID = $wpdb->term_relationships.object_id)
LEFT JOIN $wpdb->term_taxonomy ON($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id)
LEFT JOIN $wpdb->terms ON($wpdb->terms.term_id = $wpdb->term_taxonomy.term_id)
WHERE
wp_term_taxonomy.term_id IN (SELECT term_id FROM wp_terms
WHERE slug='academia' OR slug='law' OR slug='policy') AND
wp_term_taxonomy.term_id IN (SELECT term_id FROM wp_terms
WHERE slug='full-time' OR slug='part-time') AND
wp_term_taxonomy.term_id IN (SELECT term_id FROM wp_terms
WHERE slug='early-career') AND... //Rest of query/irrelevant
This hasn't produced any results. I've also tried a query like this (first 5 lines are left out as they are the same as above example):
WHERE
wp_term.slug IN ('academia','law','policy') AND
wp_term.slug IN ('full-time','part-time') AND
wp_term.slug IN ('early-career') AND ... //Rest of query
This returns results ONLY if one 'category' has selected items. It doesn't work across categories. I still think I have the query slightly wrong. I've gotten a few solutions before (both of these are solutions that I believe have gotten me the closest), but they have loopholes.
Please don't have me use HAVING COUNT because I might as well make the whole thing an AND filter in that case, which isn't what I want.
ANY help will be greatly appreciated, and if it works, I wouldn't mind working out something to make up for the trouble.
Thanks!
Taking just the WHERE clause, we can see some problems:
WHERE wp_term_taxonomy.term_id IN (SELECT term_id FROM wp_terms
WHERE slug='academia' OR slug='law' OR slug='policy')
AND wp_term_taxonomy.term_id IN (SELECT term_id FROM wp_terms
WHERE slug='full-time' OR slug='part-time')
AND wp_term_taxonomy.term_id IN (SELECT term_id FROM wp_terms
WHERE slug='early-career')
Here we have single column (wp_term_taxonomy.term_id) that has to be simultaneously the same as the term ID for one of Academia, Law or Policy (presumably 3 distinct values) and also the same as the term ID for one of Full-Time or Part-Time (presumably 2 distinct values, and different from each of the 3 previous values) and also the same as the term ID for Early-Career (one value, but distinct from each of the previous 5 values. So, the single term ID has to be 3 different values at once, and it can't manage it.
You're likely to need to join with the wp_term_taxonomy table multiple times, using 3 different aliases.
WHERE wtt1.term_id IN (SELECT term_id FROM wp_terms
WHERE slug='academia' OR slug='law' OR slug='policy')
AND wtt2.term_id IN (SELECT term_id FROM wp_terms
WHERE slug='full-time' OR slug='part-time')
AND wtt3.term_id IN (SELECT term_id FROM wp_terms
WHERE slug='early-career')
where the 3 aliases I used are wtt1, wtt2, and wtt3. They'd be listed in the JOIN conditions.
Let's look at the select list and the FROM clause
SELECT * FROM $wpdb->posts
LEFT JOIN $wpdb->postmeta ON($wpdb->posts.ID = $wpdb->postmeta.post_id)
LEFT JOIN $wpdb->term_relationships ON($wpdb->posts.ID = $wpdb->term_relationships.object_id)
LEFT JOIN $wpdb->term_taxonomy ON($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id)
LEFT JOIN $wpdb->terms ON($wpdb->terms.term_id = $wpdb->term_taxonomy.term_id)
Now let's disentangle some of the PHP material, leaving behind regular SQL:
SELECT *
FROM wp_posts AS p
LEFT JOIN wp_postmeta AS pm ON p.ID = pm.post_id
LEFT JOIN wp_term_relationships AS tr ON p.ID = tr.object_id
LEFT JOIN wp_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
LEFT JOIN wp_terms AS tm ON tm.term_id = tt.term_id
You probably do not want any left joins in here; you don't want to see posts that do not match the criteria, but using LEFT JOIN will mean that many posts get selected in this part (though all the rows are later discarded by the broken WHERE condition already discussed).
A single post may have multiple term relationship entries. We want a post that has at least three term relationship entries: one for the Academia/Law/Policy trio, one for the Full-Time/Part-Time duo, and also Early-Career.
SELECT *
FROM wp_posts AS p
JOIN wp_postmeta AS pm ON p.ID = pm.Post_ID
JOIN (SELECT t1.Object_ID
FROM wp_term_relationships AS tr
JOIN wp_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
JOIN wp_terms AS tm ON tm.term_id = tt.term_id
WHERE tm.slug IN ('academia', 'law', 'policy')
) AS t1 ON p.ID = t1.Object_ID
JOIN (SELECT t1.Object_ID
FROM wp_term_relationships AS tr
JOIN wp_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
JOIN wp_terms AS tm ON tm.term_id = tt.term_id
WHERE tm.slug IN ('full-time', 'part-time')
) AS t2 ON p.ID = t2.Object_ID
JOIN (SELECT t1.Object_ID
FROM wp_term_relationships AS tr
JOIN wp_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
JOIN wp_terms AS tm ON tm.term_id = tt.term_id
WHERE tm.slug = 'early-career')
) AS t3 ON p.ID = t3.Object_ID
I think that may do the trick — but it's getting late and I could be completely off the wall. It certainly isn't a simple query as written.
Assuming I got the basic SQL right, you simply have to replace the table names with the PHP notation for them:
SELECT *
FROM $wpdb->posts AS p
JOIN $wpdb->postmeta AS pm ON p.ID = pm.Post_ID
JOIN (SELECT t1.Object_ID
FROM $wpdb->term_relationships AS tr
JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
JOIN $wpdb->terms AS tm ON tm.term_id = tt.term_id
WHERE tm.slug IN ('academia', 'law', 'policy')
) AS t1 ON p.ID = t1.Object_ID
JOIN (SELECT t1.Object_ID
FROM $wpdb->term_relationships AS tr
JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
JOIN $wpdb->terms AS tm ON tm.term_id = tt.term_id
WHERE tm.slug IN ('full-time', 'part-time')
) AS t2 ON p.ID = t2.Object_ID
JOIN (SELECT t1.Object_ID
FROM $wpdb->term_relationships AS tr
JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
JOIN $wpdb->terms AS tm ON tm.term_id = tt.term_id
WHERE tm.slug = 'early-career')
) AS t3 ON p.ID = t3.Object_ID
You've not said which DBMS is in use, but it is likely MySQL. If you were using Oracle, you'd have to leave the AS's out of the table aliases. Standard SQL and most other SQL DBMS are fine with the AS for the table alias. Note how the use of the $wpdb-> notation is limited by the use of the table aliases; it makes the code easier to read (though it is still not an easy read).
Bug Fixing and Problem Solving
Untested code usually has bugs; this is no different from any other untested code.
The first test step was to run the sub-queries in the FROM clause in individually. That immediately showed that they should not be referencing t1.Object_ID; it should be tr.Object_ID in each case. There was also an extraneous right parenthesis after 'early-career'. These mistakes were easily spotted once I had a test database against which to run the (sub)queries.
SELECT *
FROM wp_posts AS p
JOIN wp_postmeta AS pm ON p.ID = pm.Post_ID
JOIN (SELECT tr.Object_ID
FROM wp_term_relationships AS tr
JOIN wp_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
JOIN wp_terms AS tm ON tm.term_id = tt.term_id
WHERE tm.slug IN ('academia', 'law', 'policy')
) AS t1 ON p.ID = t1.Object_ID
JOIN (SELECT tr.Object_ID
FROM wp_term_relationships AS tr
JOIN wp_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
JOIN wp_terms AS tm ON tm.term_id = tt.term_id
WHERE tm.slug IN ('full-time', 'part-time')
) AS t2 ON p.ID = t2.Object_ID
JOIN (SELECT tr.Object_ID
FROM wp_term_relationships AS tr
JOIN wp_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
JOIN wp_terms AS tm ON tm.term_id = tt.term_id
WHERE tm.slug = 'early-career'
) AS t3 ON p.ID = t3.Object_ID
With those fixes in place, the query ran and generated rows of data. You might legitimately decide you want the slugs from the three sub-queries in the results. You'd change the sub-queries to return tr.Object_ID, tm.slug. For example, this variant of the query:
SELECT p.ID, t1.slug_1, t2.slug_2, t3.slug_3, pm.meta_key
FROM wp_posts AS p
JOIN wp_postmeta AS pm ON p.ID = pm.Post_ID
JOIN (SELECT tr.Object_ID, tm.slug AS slug_1
FROM wp_term_relationships AS tr
JOIN wp_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
JOIN wp_terms AS tm ON tm.term_id = tt.term_id
WHERE tm.slug IN ('academia', 'law', 'policy')
) AS t1 ON p.ID = t1.Object_ID
JOIN (SELECT tr.Object_ID, tm.slug AS slug_2
FROM wp_term_relationships AS tr
JOIN wp_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
JOIN wp_terms AS tm ON tm.term_id = tt.term_id
WHERE tm.slug IN ('full-time', 'part-time')
) AS t2 ON p.ID = t2.Object_ID
JOIN (SELECT tr.Object_ID, tm.slug AS slug_3
FROM wp_term_relationships AS tr
JOIN wp_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
JOIN wp_terms AS tm ON tm.term_id = tt.term_id
WHERE tm.slug = 'early-career'
) AS t3 ON p.ID = t3.Object_ID;
produced the following result on some test data:
1575 policy full-time early-career date_legible
1575 policy full-time early-career date_timestamp
1575 policy full-time early-career longitude
1575 policy full-time early-career date_normal
1575 policy full-time early-career url
1575 policy full-time early-career _su_rich_snippet_type
1575 policy full-time early-career _edit_last
1575 policy full-time early-career expiration-date
1575 policy full-time early-career organization
1575 policy full-time early-career latitude
1575 policy full-time early-career location
1575 policy full-time early-career _edit_lock
This shows that there is at least one post (ID = 1575) that has the three traits you require, but it also shows that you're going to have to deal with the PostMeta data more cleverly. The result rather suggests that the PostMeta is an EAV (Entity-Attribute-Value) model. This is going to require careful handling to pull useful information (such as latitude and longitude) for the given posting. Indeed, you are going to need one (possibly outer) join for each separate meta-attribute that you want to examine.
For example, to collect the latitude and longitude, if available, for the post, you'd need to write:
SELECT p.ID, t1.slug_1, t2.slug_2, t3.slug_3, p1.latitude, p2.longitude
FROM wp_posts AS p
LEFT JOIN
(SELECT Post_ID, Meta_Key AS m1_key, Meta_Value AS latitude
FROM wp_postmeta
WHERE Meta_Key = 'latitude'
) AS p1 ON p.ID = p1.Post_ID
LEFT JOIN
(SELECT Post_ID, Meta_Key AS m2_key, Meta_Value AS longitude
FROM wp_postmeta
WHERE Meta_Key = 'longitude'
) AS p2 ON p.ID = p2.Post_ID
JOIN (SELECT tr.Object_ID, tm.slug AS slug_1
FROM wp_term_relationships AS tr
JOIN wp_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
JOIN wp_terms AS tm ON tm.term_id = tt.term_id
WHERE tm.slug IN ('academia', 'law', 'policy')
) AS t1 ON p.ID = t1.Object_ID
JOIN (SELECT tr.Object_ID, tm.slug AS slug_2
FROM wp_term_relationships AS tr
JOIN wp_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
JOIN wp_terms AS tm ON tm.term_id = tt.term_id
WHERE tm.slug IN ('full-time', 'part-time')
) AS t2 ON p.ID = t2.Object_ID
JOIN (SELECT tr.Object_ID, tm.slug AS slug_3
FROM wp_term_relationships AS tr
JOIN wp_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
JOIN wp_terms AS tm ON tm.term_id = tt.term_id
WHERE tm.slug = 'early-career'
) AS t3 ON p.ID = t3.Object_ID;
Which produces:
1575 policy full-time early-career -33.8210366 151.1887557
Etcetera.
Change your IN to EXISTS; try it out — you will get your result.
WHERE
wp_term_taxonomy.term_id exists (SELECT term_id FROM wp_terms
WHERE slug='academia' OR slug='law' OR slug='policy') AND
wp_term_taxonomy.term_id exists(SELECT term_id FROM wp_terms
WHERE slug='full-time' OR slug='part-time') AND
wp_term_taxonomy.term_id exists (SELECT term_id FROM wp_terms
WHERE slug='early-career') AND... //Rest of query/irrelevant
Use OR:
wp_term_taxonomy.term_id IN (SELECT term_id FROM wp_terms
WHERE slug='academia' OR slug='law' OR slug='policy')
OR wp_term_taxonomy.term_id IN (SELECT term_id FROM wp_terms
WHERE slug='full-time' OR slug='part-time')
OR wp_term_taxonomy.term_id IN (SELECT term_id FROM wp_terms
WHERE slug='early-career') AND... //Rest of query/irrelevan
Analyzing your query, shouldn't it be merely:
wp_term_taxonomy.term_id IN (SELECT term_id FROM wp_terms
WHERE slug='academia' OR slug='law' OR slug='policy'
OR slug='full-time' OR slug='part-time'
OR slug='early-career')
Related
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.
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
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'";
I can get links from the WordPress database that are in a specific category like this (I have added line breaks to the SQL part for clarity):
$category1 = 'stuff';
$category2 = 'other_stuff';
$q = 'select * from wp_links l
inner join wp_term_relationships r on l.link_id = r.object_id
inner join wp_term_taxonomy using (term_taxonomy_id)
inner join wp_terms using (term_id)
where taxonomy = "link_category"
and name = '.$category1;
$results = $wpdb->get_results($q);
How would I retrieve links that are in both $category1 and $category2 (I do mean both categories, not either category)?
On the assumption that the name field you are searching on is in the wp_terms table, there are two principle options.
The first just looks for entities with the first category, then uses another join the find those that also have the second category. This involes using the same table in two different joins, and so uses aliases and avoids the using keyword.
select * from wp_links l
inner join wp_term_relationships r on l.link_id = r.object_id
inner join wp_term_taxonomy t on r.term_taxonomy_id = t.term_taxonomy_id
inner join wp_terms c1 on t.term_id = c1.term_id
inner join wp_terms c2 on t.term_id = c2.term_id
where taxonomy = "link_category"
and c1.name = 'stuff'
and c2.name = 'other_stuff'
The alternative is much more scalable to more than just two catagories, but involves a sub-query...
select
l.*
from
(
select l.id from wp_links l
inner join wp_term_relationships r on l.link_id = r.object_id
inner join wp_term_taxonomy t on r.term_taxonomy_id = t.term_taxonomy_id
inner join wp_terms c on t.term_id = c.term_id
where taxonomy = "link_category"
and c.name IN ('stuff', 'other_stuff')
group by l.id
having count(distinct c.name) = 2
)
subquery
inner join wp_links l ON subquery.id = l.id
The inner query finds all that have one category or the other, but then the having clause only lets through those that have two categories in our list. (In other words, both of them.) [It also assumes that the wp_links table as an id column to use as a unique identifier.]
did you try with the OR?
$q = 'select * from (select * from wp_links l
inner join wp_term_relationships r on l.link_id = r.object_id
inner join wp_term_taxonomy using (term_taxonomy_id)
inner join wp_terms using (term_id)
where taxonomy = "link_category"
and name = '.$category1.') as t1 join (select * from wp_links l
inner join wp_term_relationships r on l.link_id = r.object_id
inner join wp_term_taxonomy using (term_taxonomy_id)
inner join wp_terms using (term_id)
where taxonomy = "link_category"
and name = '.$category2 .') as t2
on t1.term_id=t2.term_id;
I don't think this is the best solution, but you don't give me more details about your structure, tables what you have
This is a very specific question regarding MySQL as implemented in WordPress.
I'm trying to develop a plugin that will show (select) posts that have specific 'tags' and belong to specific 'categories' (both multiple)
I was told it's impossible because of the way categories and tags are stored:
wp_posts contains a list of posts, each post have an "ID"
wp_terms contains a list of terms (both categories and tags). Each term has a TERM_ID
wp_term_taxonomy has a list of terms with their TERM_IDs and has a Taxonomy definition for each one of those (either a Category or a Tag)
wp_term_relationships has associations between terms and posts
How can I join the tables to get all posts with tags "Nuclear" and "Deals" that also belong to the category "Category1"?
I misunderstood you. I thought you wanted Nuclear or Deals. The below should give you only Nuclear and Deals.
select p.*
from wp_posts p, wp_terms t, wp_term_taxonomy tt, wp_term_relationship tr,
wp_terms t2, wp_term_taxonomy tt2, wp_term_relationship tr2
wp_terms t2, wp_term_taxonomy tt2, wp_term_relationship tr2
where p.id = tr.object_id and t.term_id = tt.term_id and tr.term_taxonomy_id = tt.term_taxonomy_id
and p.id = tr2.object_id and t2.term_id = tt2.term_id and tr2.term_taxonomy_id = tt2.term_taxonomy_id
and p.id = tr3.object_id and t3.term_id = tt3.term_id and tr3.term_taxonomy_id = tt3.term_taxonomy_id
and (tt.taxonomy = 'category' and tt.term_id = t.term_id and t.name = 'Category1')
and (tt2.taxonomy = 'post_tag' and tt2.term_id = t2.term_id and t2.name = 'Nuclear')
and (tt3.taxonomy = 'post_tag' and tt3.term_id = t3.term_id and t3.name = 'Deals')
Try this:
select p.*
from wp_posts p,
wp_terms t, wp_term_taxonomy tt, wp_term_relationship tr
wp_terms t2, wp_term_taxonomy tt2, wp_term_relationship tr2
where p.id = tr.object_id
and t.term_id = tt.term_id
and tr.term_taxonomy_id = tt.term_taxonomy_id
and p.id = tr2.object_id
and t2.term_id = tt2.term_id
and tr2.term_taxonomy_id = tt2.term_taxonomy_id
and (tt.taxonomy = 'category' and tt.term_id = t.term_id and t.name = 'Category1')
and (tt2.taxonomy = 'post_tag' and tt2.term_id = t2.term_id and t2.name in ('Nuclear', 'Deals'))
Essentially I'm employing 2 copies of the pertinent child tables - terms, term_taxonomy, and term_relationship. One copy applies the 'Category1' restriction, the other the 'Nuclear' or 'Deals' restriction.
BTW, what kind of project is this with posts all about nuclear deals? You trying to get us on some government list? ;)
What a gross DB structure.
Anyway, I'd do something like this (note I prefer EXISTS to joins, but you can re-write them as joins if you like; most query analyzers will collapse them to the same query plan anyway). You may have to do some additional juggling one way or another to make it work...
SELECT *
FROM wp_posts p
WHERE EXISTS( SELECT *
FROM wp_term_relationship tr
WHERE tr.object_id = p.id
AND EXISTS( SELECT *
FROM wp_term_taxonomy tt
WHERE tt.term_taxonomy_id = tr.term_taxonomy_id
AND tt.taxonomy = 'category'
AND EXISTS( SELECT *
FROM wp_terms t
WHERE t.term_id = tt.term_id
AND t.name = "Category1"
)
)
AND EXISTS( SELECT *
FROM wp_term_taxonomy tt
WHERE tt.term_taxonomy_id = tr.term_taxonomy_id
AND tt.taxonomy = 'post_tag'
AND EXISTS( SELECT *
FROM wp_terms t
WHERE t.term_id = tt.term_id
AND t.name = "Nuclear"
)
AND EXISTS( SELECT *
FROM wp_terms t
WHERE t.term_id = tt.term_id
AND t.name = "Deals"
)
)
)
So I tried both options on my WordPress db. I looked for the category "Tech" in my posts with the tags "Perl" AND "Programming".
Eric's worked once I added a missing comma in the initial select statement. It returned 3 records. The problem is that the section that is looking for the "post_tag" is actually working as an OR option. One of my posts only had one tag not both. Also it would be good to make the SELECT DISTINCT.
I tried Matt's version, but it kept returning an empty set. I may try to "juggle" with it.
Thanks #Eric it works! Just a few code corrections for future reference:
the first select statements misses a coma after wp_term_relationship tr2
In the same select statemt the following must be change:
wp_terms t2, wp_term_taxonomy tt2, wp_term_relationship
tr2
should be
wp_terms t3, wp_term_taxonomy tt3, wp_term_relationship
tr3
Really so great answer .. helped me a lot..
great bcoz., it gave me basic approach to build my complex query !
one small correction, for ready users like me :)
"wp_term_relationship" will give 'doesn't exist error'
.. use wp_term_relationships as it is the correct table name.
Thanks Eric