Mysql select from multiple tables based on like count - php

I have this problem with my sql statement and am not sure how to get around it.
I am using WordPress and the WTI Like Post plugin
Here is the query:
SELECT wpblog_posts.ID, wpblog_posts.post_title, wpblog_posts.post_excerpt, wpblog_posts.guid, wpblog_posts.post_author, wpblog_wti_like_post.value, wpblog_wti_like_post.post_id
FROM wpblog_posts, wpblog_wti_like_post
LEFT JOIN wpblog_term_relationships rel ON rel.object_id = wpblog_posts.ID
LEFT JOIN wpblog_term_taxonomy tax ON tax.term_taxonomy_id = rel.term_taxonomy_id
LEFT JOIN wpblog_terms t ON t.term_id = tax.term_id WHERE t.term_id = 165
AND wpblog_posts.post_type = 'post'
AND wpblog_wti_like_post.post_id = wpblog_posts.ID
ORDER BY wpblog_wti_like_post.value
LIMIT 20
Here is a screen shot of the wpblog_wti_like_post table:
So what i'm trying to do is select all posts from a category, and order those posts by wpblog_wti_like_post.value
Now in the category some posts are liked and disliked, and appear in the table wpblog_wti_like_post
but some posts don't appear in that table.
How would I:
a. Select all posts in that category
b. Order by wpblog_wti_like_post.value
I am completely stumped on how to accomplish this.
Cheers
UPDATE
This query selects all posts from a specific category and works fine, but the query above doesn't:
SELECT wpblog_posts.ID, wpblog_posts.post_title, wpblog_posts.post_excerpt, wpblog_posts.guid, wpblog_posts.post_author
FROM wpblog_posts
LEFT JOIN wpblog_term_relationships rel ON rel.object_id = wpblog_posts.ID
LEFT JOIN wpblog_term_taxonomy tax ON tax.term_taxonomy_id = rel.term_taxonomy_id
LEFT JOIN wpblog_terms t ON t.term_id = tax.term_id
WHERE t.term_id = 165
LIMIT 20

Tip: something like the following is more readable...
SELECT p.ID
, p.post_title
, p.post_excerpt
, p.guid
, p.post_author
FROM wpblog_posts p
LEFT
JOIN wpblog_term_relationships rel
ON rel.object_id = p.ID
LEFT
JOIN wpblog_term_taxonomy tax
ON tax.term_taxonomy_id = rel.term_taxonomy_id
LEFT
JOIN wpblog_terms t
ON t.term_id = tax.term_id
WHERE t.term_id = 165
LIMIT 20
Some observations on the above:
LEFT JOIN x... WHERE x = is the same as INNER JOIN x
There is no point LEFT JOINing tables from which you select no columns. As stated, the above is in fact an inner join, so this fact is for future reference only.
LIMIT without ORDER BY is fairly meaningless

Related

Slow mysql query using Doctrine

To filter the products that belongs to the selected filters, I wrote this query below. When i run this in Mysql, it is very fast. But when i'm trying to execute this query in Doctrine, it is very slow. Removing the group by gives me one result and than it is fast, but it has to be 11 results.
Even the more filters i add to this statement, the slower it gets.
Can anyone help me out with this problem?
SELECT DISTINCT p.id, p.brand_id, p.name, p.permalink, p.base_model,
p.ean, p.description, p.unit, p.price, p.offer_active, p.offer_price,
GROUP_CONCAT(DISTINCT m.url ORDER BY `m`.`order`, `p`.`id` SEPARATOR ",") AS url, pcm.category_id
FROM product p
LEFT JOIN product_category pc ON pc.product_id = p.id
LEFT JOIN media m ON p.id = m.product_id
INNER JOIN product_category pcm ON p.id = pcm.product_id AND pcm.main = 1 AND pcm.product_id IS NOT NULL
LEFT JOIN product_filter_value pfv1 ON p.id = pfv1.product_id
LEFT JOIN product_filter_value pfv2 ON p.id = pfv2.product_id
LEFT JOIN product_filter_value pfv3 ON p.id = pfv3.product_id
LEFT JOIN product_filter_value pfv4 ON p.id = pfv4.product_id
WHERE (pc.category_id = 23)
AND (p.active = 1)
AND (pfv1.filter_value_id IN (2))
AND (pfv2.filter_value_id IN (8))
AND (pfv3.filter_value_id IN (22))
AND (pfv4.filter_value_id IN (38))
GROUP BY 1
ORDER BY p.top desc

Count user posts

I am listing the tags/categories on the user's page. I would like to show the number of posts the user made for each tag. The tags, the posts, and the post-tags are in different tables.
The difficulty is, that there are two kind of posts. The posts, and the comments. They are in the same table, but different type. "question" and "answer". the related_id at the answers are the id of the posts they are related to.
I tried to solve in pretty lot of way but couldn't get it to work.
My db structures:
For tags:
tagid tag_name
For posts
id type(enum:"question","answer") related_id user_id
For post-tags:
post_id tag_id
The code what I tried is the following:
$user_active_query = mysql_query("select p.id,
p.user_id,
pt.post_id,
count(pt.post_id),
pt.tag_id,
t.tagid,
t.tag_name
from posts p
inner join post_tags pt
inner join tags t
on p.id = pt.post_id
and pt.tag_id = t.tagid
where p.user_id = '$uid'
group by t.tagid");
while($useractive = mysql_fetch_array($user_active_query)) {
$user_active_counter = $useractive['count(pt.post_id)'];
echo "<a href='' class='btn btn-mini' style='margin:3px;'>".$useractive['tag_name']." (".$user_active_counter.")</a>";
}
User id is given on the page. "$uid". I am just tired of the lot of try and asking for correction. First it seemed to be the best way to store the post-tags but now this is a nightmare. I mean, for me, its seems impossible to do this with this structure.
You can get both counts i.e the no of answers and no of questions posted by a user ,here is the trick also use proper join syntax you are missing the on clause for join
SELECT
p.id,
p.user_id,
pt.post_id,
COUNT(pt.post_id) all_posts,
COALESCE(SUM(`type` = 'question')) questions,
COALESCE(SUM(`type` = 'answer')) answers,
pt.tag_id,
t.tagid,
t.tag_name
FROM tags t
LEFT JOIN post_tags pt ON(pt.tag_id = t.tagid)
LEFT JOIN posts p ON p.id = pt.post_id
WHERE p.user_id = '$uid'
GROUP BY t.tagid
Note in mysql sum with some expression will result in a boolean
Edit from comments add another condition using OR in your last join so first condition will join the posts that are associated with tags ,and as your explanation tags are not directly linked with answers but answer are linked to their question with related id so can join the related id of each answer to tag id so this way can get the tags for answers too
SELECT
p.id,
p.user_id,
pt.post_id,
COUNT(pt.post_id) all_posts,
COALESCE(SUM(`type` = 'question')) questions,
COALESCE(SUM(`type` = 'answer')) answers,
pt.tag_id,
t.tagid,
t.tag_name
FROM tags t
LEFT JOIN post_tags pt ON(pt.tag_id = t.tagid)
LEFT JOIN posts p ON (p.id = pt.post_id OR p.related_id = pt.post_id)
WHERE p.user_id = '$uid'
GROUP BY t.tagid
I think you only need to include p.type in your group by clause
$user_active_query = mysql_query("
select
p.id,
p.user_id,
pt.post_id,
count(pt.post_id),
pt.tag_id,
t.tagid,
t.tag_name
from posts p
inner join post_tags pt
inner join tags t
on p.id = pt.post_id
and pt.tag_id = t.tagid
where p.user_id = '$uid'
group by t.tagid, p.type"
);
so, we will group per type too.
I'm not sure I fully understand your schema design. But it sounds like you have two join "paths", one to get to the question type posts, and another to get to child answer type posts.
To get the count of the question-type posts (by a specific user) related to each tag, looks like what you have so far, basically:
SELECT t.tagid
, t.tag_name
, COUNT(p.id) AS count_question
FROM tags t
JOIN post_tags pt
ON pt.tag_id = t.tagid
JOIN posts p
ON p.id = pt.post_id
AND p.type = 'question'
WHERE p.user_id = '$uid'
GROUP BY t.tagid
To get the count of the answer-type posts (by a specific user) related to a question related to each tag, we first need to join to the 'question' (to get the tags), and then join to the related answer. This is nearly identical to the first query, except that we add another join to posts table to get the "child" answer-type posts (so we can get a count of the answer-type posts), and we are not restricting the question-type posts to those from the specific user... we are going to count the answer-type posts from a user posted against any user's question.
SELECT t.tagid
, t.tag_name
, COUNT(a.id) AS count_answer
FROM tags t
JOIN post_tags pt
ON pt.tag_id = t.tagid
JOIN posts p
ON p.id = pt.post_id
AND p.type = 'question'
JOIN posts a
ON a.related_id = p.id
AND a.type = 'answer'
WHERE a.user_id = '$uid'
GROUP BY t.tagid
If each of those queries returns a portion of the total count you want to return, those can be combined, but it gets a little bit messy.
The most straightforward approach is to combine those two results with a UNION ALL set operator, and then use that as an inline view, i.e. run another query against the combined resultset.
For example:
SELECT r.tagid
, r.tagname
, SUM(r.count_post) AS count_total
, SUM(IF(r.type='q',r.count_post,0)) AS count_question
, SUM(IF(r.type='a',r.count_post,0)) AS count_answer
FROM (
SELECT 'q' AS type
, t.tagid
, t.tag_name
, COUNT(p.id) AS count_post
FROM tags t
JOIN post_tags pt
ON pt.tag_id = t.tagid
JOIN posts p
ON p.id = pt.post_id
AND p.type = 'question'
WHERE p.user_id = '$uid'
GROUP BY t.tagid
UNION ALL
SELECT 'a' AS type
, t.tagid
, t.tag_name
, COUNT(a.id) AS count_post
FROM tags t
JOIN post_tags pt
ON pt.tag_id = t.tagid
JOIN posts p
ON p.id = pt.post_id
AND p.type = 'question'
JOIN posts a
ON a.related_id = p.id
AND a.type = 'answer'
WHERE a.user_id = '$uid'
GROUP BY t.tagid
) r
GROUP BY r.tag_id
If you aren't interested in the individual counts (of question and answer type posts), then just remove those two expressions from the SELECT list of the outer query, and you can also remove the type='q', type='a' discriminator column from the inline view query.
This isn't the only way to combine the results, but I think it's the easiest way to verify we're getting a "correct" result (we can run just the inline view query and verify that the results from that are correct.
Another approach to combining them is messier, and more difficult to decipher.
We basically need to join to question-type posts from all users, and then do an outer join operation to the answer-type posts from the specific user.
We can use predicates in the WHERE clause to filter out the rows, so that return only rows that have a matching answer-type row -OR- are a question-type row poseted by the specified user.
In the SELECT list, we need to do some additional filtering, so that we filter out posts from other users.
Something like this:
SELECT t.tagid
, t.tag_name
, COUNT(DISTINCT IF(p.user_id='$uid',p.id,NULL))
+ COUNT(DISTINCT a.id) AS count_total
, COUNT(DISTINCT IF(p.user_id='$uid',p.id,NULL)) AS count_question
, COUNT(DISTINCT a.id) AS count_question
FROM tags t
JOIN post_tags pt
ON pt.tag_id = t.tagid
JOIN posts p
ON p.id = pt.post_id
AND p.type = 'question'
LEFT
JOIN posts a
ON a.related_id = p.id
AND a.type = 'answer'
AND a.user_id = '$uid'
WHERE p.user_id = '$uid'
OR a.id IS NOT NULL
GROUP BY t.tagid
But, I'd don't really like this query, it's too hard to figure out what's going on. I'd opt for the (previous) query, with the UNION ALL inline view. That's easier to decipher.

LEFT JOIN only returning half the needed results

I currently have:
SELECT * FROM submissions AS s
LEFT JOIN submission_category AS sc ON s.submission_category = sc.sub_cat_id
JOIN hospitals AS h ON h.hospital_id = sc.main_hospital_id
JOIN products AS p ON p.product_id = s.product_id
JOIN product_group AS pg ON pg.id = p.product_group_id
JOIN progress_steps AS ps ON sc.approval_step = ps.step_id
WHERE s.approval_status='2'
AND sc.user_id='$mask5'
The problem is that sc has 3 rows and s has 6 rows. I need to return all six rows but currently it is only returning 3, one of each from s that corresponds to sc.
Based on the suggestion I am now trying:
SELECT * FROM submissions AS s
LEFT JOIN submission_category AS sc ON (s.submission_category = sc.sub_cat_id AND sc.user_id='$mask5' )
JOIN hospitals AS h ON h.hospital_id = sc.main_hospital_id
JOIN products AS p ON p.product_id = s.product_id
JOIN product_group AS pg ON pg.id = p.product_group_id
JOIN progress_steps AS ps ON sc.approval_step = ps.step_id
WHERE s.approval_status='2'
But still getting three rows, not six?
Having columns of LEFT JOINed table[s] in WHERE "turns" LEFT JOIN into INNER (sc.user_id = '$mask5' always returns NULL or false in case there is no corresponding row in submission_category ). Move sc.user_id=... into join condition :
....
LEFT JOIN submission_category AS sc ON (s.submission_category = sc.sub_cat_id AND
sc.user_id='$mask5' )
...

SQL query for wp_links in multiple categories

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

How to query 3 tables in a single query?

I'm really sorry for the first post as i didn't explain everything.
Basically i have 3 tables, One for posts, One for Categories, & Another to link categories with posts.
I want in a single MySQL query to select posts that are under a specific category.
posts(id,title,body)
---------------------
125,Some title,Blah blah
categories(id,name)
---------------------
1,politic
2,entertainment
linker(categoryid,postid)
---------------------
2,125
I want in single query to fetch posts in the entertainment category by example, what to do?
Thanks
select
p.*
from
posts p
inner join linker l on l.postid = p.id
inner join categories c on c.categoryid = l.categoryid
where
c.name = 'entertainment'
The following SQL statement should provide you with a basis for what you are trying to do.
select p.*
from posts p
inner join linker l
on l.postid = p.id
inner join categories c
on l.categoryid = c.id
where c.name = 'entertainment'
If a post belongs to 2 categories, you can still use pinkfloydx33's query with DISTINCT in the select statement:
select
DISTINCT p.*
from
posts p
inner join linker l on l.postid = p.id
inner join categories c on c.categoryid = l.categoryid
where
c.name = 'entertainment'
The result set will show only one record.
It's the same exact thing, you just have to join 3 tables intead of 2 :
SELECT P.id post_id,
P.title,
P.body,
C.id category_id,
C.name
FROM posts P
INNER JOIN linker L
ON P.id = L.postid
INNER JOIN categories C
ON L.categoryid = C.id
WHERE C.name = 'Category'
Don't be afraid to do your own tests. If you understand how to join two tables, you should understand how to join three, four and more.
If you are specifying only one category in the WHERE clause, then the result will be a single row for each post ID.
Either way you can use DISTINCT or GROUP BY when the result could be more than one row per ID, but in that case i prefer the second one (GROUP BY).

Categories