sql skip result based on value - sql

Lets say I have a bunch of images/post, where I join images metadata.
If _wp_attachment_image_alt does not have a value, then it should skip the result. Please note that it should skip the entire result of that post/image, and not just the row.
What I have tried is this:
SELECT
id,
post_title,
guid,meta_key
FROM
posts a
LEFT JOIN
postmeta b ON a.id = b.post_id
WHERE
post_type = 'attachment'
AND
meta_key NOT LIKE '_wp_attachment_image_alt'
limit 20
This kinda works but not what I would like it to. It should skip the post from wcuq_post, and not just one row of wcuq_postmeta.

SELECT
id,
post_title,
guid,
meta_key
FROM
posts a
LEFT JOIN
postmeta b ON a.id = b.post_id
WHERE
post_type = 'attachment'
AND id IN (SELECT post_id FROM postmeta WHERE meta_key = '_wp_attachment_image_alt' AND meta_value != '')
LIMIT 20

Related

Group by on join and calculate max of groups

I have 3 tables as described below:
All three tables and output image
posts post table
post_comments posts comments
comments comments
Now I want to fetch the posts that have highest liked comments and the status of that comment should be active in Postgres.
OUTPUT:
posts resultant posts
NOTE: Since for post 1, the highest liked comment is inactive.
I've tried something like this:
select "posts".*
from "posts"
inner join (select id, max(likes) l from comments innner join post_comments on comments.id = post_comments.alert_id and post_comments.post_id = posts.id) a on posts.id = a.cid ...
This is not complete but I'm unable to do this.
In Postgres, you can get the active comment with the most likes for each post using distinct on:
select distinct on (pc.post_id) pc.*
from post_comments pc join
comments c
on pc.comment_id = c.id
where c.status = 'active'
order by pc.post_id, c.likes desc;
I think this is quite related to what you want.
Try something like this:
SELECT posts.*, MAX(likes) l
FROM posts
JOIN post_comments ON post_id = posts.id
LEFT JOIN comments ON comment_id = comments.id
GROUP BY posts.id

Get tag and category posts WP SQL

I am a ios developer and have some problems with sql queries.
I get wordpress posts with query like:
SELECT id, post_title, post_content FROM `posts`
WHERE post_status = 'publish' AND post_type = 'post';
But I need to get some tag posts and category posts like:
SELECT id, post_title, post_content FROM `posts` WHERE category = '1';
and
SELECT id, post_title, post_content FROM `posts` WHERE tag = '1';
I got that tags and categories are in terms table. How can I connect two tables and get needed posts.
You need to look in wp_terms table to get term_id for your tag, then look in wp_term_taxonomy table to convert to term_taxonomy_id. This can then be used to filter the wp_term_relationships table to get a list of post ids. Here is some SQL to get a list of post ids from either a tag or category name
SELECT object_id FROM wp_term_relationships
INNER JOIN wp_term_taxonomy ON wp_term_taxonomy.term_taxonomy_id=wp_term_relationships.term_taxonomy_id
INNER JOIN wp_terms ON wp_terms.term_id=wp_term_taxonomy.term_id
WHERE name='tag,category'
To get actual posts you could use each object_id to filter wp_posts eg
SELECT ID,post_title,post_content FROM wp_posts WHERE ID='object_id'
or you could do it in one query to get all posts with specified tag,category
SELECT ID,post_title,post_content FROM wp_posts
INNER JOIN wp_term_relationships ON wp_term_relationships.object_id=wp_posts.ID
INNER JOIN wp_term_taxonomy ON wp_term_taxonomy.term_taxonomy_id=wp_term_relationships.term_taxonomy_id
INNER JOIN wp_terms ON wp_terms.term_id=wp_term_taxonomy.term_id
WHERE name='tag,category'

PostgreSQL NOT IN does not work correctly with JOIN

I have trivial tables post, tag and post_tags in a trivial Many-To-Many relationship. I want to select some posts by including and excluding some tags. I tried many variations of SQL queries, but none of them works for excluding tags.
I started from a query like this:
SELECT post.* FROM post
INNER JOIN post_tags ON post.id = post_tags.post_id
INNER JOIN tag ON post_tags.tag_id = tag.id
WHERE tag.name IN ('Science','Culture')
AND tag.name NOT IN ('War', 'Crime')
GROUP BY post.id
HAVING COUNT(post_tags.id) > 1
ORDER BY post.rating DESC
LIMIT 50;
But, unfortunately, this does not work. I see posts with tag "War" in result set. Then I tried to move the NOT IN condition to a separate subquery on post_tags and join to it:
SELECT post.* FROM post
INNER JOIN post_tags ON post.id = post_tags.post_id
INNER JOIN (SELECT * FROM tag WHERE name NOT IN ('War', 'Crime')) AS tags
ON post_tags.tag_id = tags.id
WHERE tags.name IN ('Science','Culture')
GROUP BY post.id
HAVING COUNT(post_tags.id) > 1
ORDER BY post.rating DESC
LIMIT 50;
Even tried to exclude some posts in first JOIN like this:
SELECT post.* FROM post
INNER JOIN post_tags ON post.id = post_tags.post_id
AND post_tags.tag_id NOT IN (SELECT id FROM tag WHERE name IN ('War', 'Crime'))
INNER JOIN tag ON post_tags.tag_id = tag.id
WHERE tag.name IN ('Science','Culture')
GROUP BY post.id
HAVING COUNT(post_tags.id) > 1
ORDER BY post.rating DESC
LIMIT 50;
But none of this works. I am especially confused about second query (joining with filtered result set instead of table).
Using PostgreSQL version 9.3, OS Ubuntu 14.04.
Any thoughts?
It is working fine. It is your logic that is off. You are filtering out the very tags that you want to check for. So, they are not part of the check.
Instead, move the conditions to the having clause:
SELECT p.*
FROM post p INNER JOIN
post_tags pt
ON p.id = pt.post_id INNER JOIN
tag t
ON pt.tag_id = t.id
WHERE t.name IN ('Science', 'Culture', 'War', 'Crime')
GROUP BY p.id
HAVING SUM(CASE WHEN t.name IN ('Science', 'Culture') THEN 1 ELSE 0 END) > 1 AND
SUM(CASE WHEN t.name IN ('War', 'Crime') THEN 1 ELSE 0 END) = 0
ORDER BY p.rating DESC;
There is a difference between ignoring a value (in the where clause) versus checking that it is not there (in the having clause).
This is an application of relational-division. Check out the tag description.
You have to define what you want exactly. Posts with one of the "good" tags and none of the "bad" tags? Or all of the good tags?
The best query technique depends on the table layout. Typically we'd assume referential integrity and that (post_id, tag_id) is defined unique in post_tags, but that's not defined.
Assuming that, and describing your problem as:
Return the 50 posts with the highest rating with at least one of the tags ('Science','Culture') and none of the tags ('War', 'Crime').
We can translate this plain English sentence into SQL directly:
SELECT p.*
FROM post p
WHERE EXISTS ( -- at least one of the tags ('Science','Culture')
SELECT 1
FROM tag t
JOIN post_tags pt ON pt.tag_id = t.id
WHERE pt.post_id = p.id
AND t.name IN ('Science', 'Culture')
AND NOT EXISTS ( -- none of the tags ('War', 'Crime')
SELECT 1
FROM tag t
JOIN post_tags pt ON pt.tag_id = t.id
WHERE pt.post_id = p.id
AND t.name IN ('War', 'Crime')
ORDER BY p.rating DESC -- with the highest rating
LIMIT 50; -- 50 posts
This is typically faster than grouping rows and counting - and also works if (post_id, tag_id) is not unique.
More techniques for relational division:
How to filter SQL results in a has-many-through relation

Wordpress: SQL Query to add custom field to posts with specific tag

I'm trying to find a way to add a custom field to all my wordpress post that has a specific tag.
I have to add "meta_value = us" to all post that has a "apple" tag.
TABLE: wp_postmeta
meta_id = ...
Post_id = ...
meta_key = meta_country
meta_value = it
I don't find where tag is stored.
Someone can help me with the correct query that i have to run to do that?
All taxonomy terms, of which "tags" are, and assuming a "wp_" prefix are stored in the wp_terms table, which is related to wp_term_taxonomy, which is related to wp_term_relationships which is related to wp_posts. You can insert into the wp_postmeta table using the results of a queries on the former tables.
INSERT INTO wp_postmeta ( `post_id`, `meta_key`, `meta_value` )
SELECT tr.object_id, 'meta_key' AS meta_key, 'meta_value' AS meta_value
FROM wp_terms t
JOIN wp_term_taxonomy tt ON tt.term_id = t.term_id
JOIN wp_term_relationships tr ON tr.taxonomy_id = tt.taxonomy_id
WHERE t.name = 'apple'

what is the query to get "related tags" like in stack overflow

i have 3 tables:
links (id, linkName)
tags (id, tagName)
tagsBridge (tagID, linkID)
i am trying to support showing related tags like in SOF. so if you click on tags "XYZ", right now i am showing all the links with tag "XYZ" but i also want to show the distinct list of all other tags that people have tagged those items that also have tagged" "XYZ"
what is the fastest way to query this
Try:
SELECT t.tagname
FROM TAGS t
JOIN TAGS_BRIDGE tb ON tb.tagid = t.id
JOIN (SELECT li.id
FROM LINKS li
JOIN TAGS_BRIDGE tb ON tb.linkid = li.id
JOIN TAGS t ON t.id = tb.tagid
WHERE t.tagname = 'XYZ') x ON x.id = tb.linkid
GROUP BY t.tagname
A very ugly nested query.
SELECT DISTINCT tagName FROM tags WHERE id in
(
SELECT tagID FROM tagsBridge WHERE linkID IN
(
SELECT linkID FROM tagsBridge WHERE tagID IN
(
SELECT id FROM tags WHERE tagName like 'XYZ'
)
)
)
Edited: now this is basically is just a different way of writing Kirk Broadhurst's, I think. I guess some DB might handle it differently behind the scenes, but I think almost all modern engines would end up with the two of them having the same query plan.
select distinct t.tagName
from tags t
join tagsBridge tb on (t.id = tb.tagID)
join tagsBridge tbt on (tb.linkID = tbt.linkID)
join tags ta on (ta.id = tbt.tagID)
where ta.tagname = 'XYZ'