Join tables (mysql, php) - php

I have these tables:
photos (photo_id, photo_title, path)
photos_tags (id, photo_id, tag_id)
tags (tag_id, tag_title).
On my page I am displaying photos, their titles and I want to show tags for each photo, but I don't know how.
I think I should use LEFT JOIN to join tables photos_tags and tags but I don't know where to start. Any help would be appreciated!

Select
photos.photo_title,
photos.path,
group_concat(tags.tag_title) as tags
from photos
inner join photos_tags on photos.photo_id = photos_tags.photo_id
inner join tags on tags.tag_id = photos_tags.tag_id
group by photos.photo_id
If you want to get photos without tags as well then you should use left join instead.
If you have a lot of tags or you would like to get more data than tag name then you should prepare 2 queries. Second with in operator containing list of photos ids. So, firstly get all the photos
select * from photos
then get all the ids and pass to second query
select * from tags
inner join photos_tags on photos_tags.tag_id = tags.tag_id
where photos_tags.photo_id in (1,2,3,4)

You need left joins for all photos with their corresponding tags.
Do like this:
SELECT P.*, T.tag_title FROM photos P
LEFT JOIN photos_tags PT ON P.photo_id = PT.photo_id
LEFT JOIN tags T ON T.tag_id = PT.tag_id
Here you get all the tags, photo id wise.
Let me know for further help.

Assuming you've got your photo id ($yourPhotoId)
a query for the specific photo would be something like this:
SELECT t.tag_id, t.tag_title
FROM photo_tags pt
LEFT JOIN tags t ON t.tag_id = pt.tag_id
WHERE pt.photo_id = {$yourPhotoId}
and for all photos(and every columns):
SELECT *
FROM photos p
LEFT JOIN photo_tags pt ON pt.photo_id = p.photo_id
LEFT JOIN tags t ON t.tag_id = pt.tag_id

Specific Photo would be something like this:
//photo_id just example
$photo_id = '1';
SELECT pt.id as id, pt.photo_id as photo_id, pt.tag_id as tag_id, t.tag_title, p.photo_title, p.path FROM photos p
INNER JOIN photo_tags pt ON pt.photo_id = $photo_id
INNER JOIN tags t ON t.tag_id = pt.tag_id
All Phosts:
SELECT pt.id as id, pt.photo_id as photo_id, pt.tag_id as tag_id, t.tag_title, p.photo_title, p.path FROM photos p
INNER JOIN photo_tags pt ON pt.photo_id = p.photo_id
INNER JOIN tags t ON t.tag_id = pt.tag_id

Try this
SELECT P.*, T.tag_title FROM photos P
JOIN photo_tags PT ON P.photo_id = PT.photo_id
LEFT JOIN tags T ON T.tag_id = PT.tag_id
WHERE t.tag_id = 1
SEE DEMO

Related

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.

How to filter result with two tags parameters in MySQL?

I have this SQl query;
SELECT prod.* FROM bb_admin.bb_directory_products as prod
LEFT JOIN bb_admin.bb_map_product_category as mapcat ON prod.product_id = mapcat.product_id
LEFT JOIN bb_admin.bb_categories_products as cat ON mapcat.category_id = cat.category_id
LEFT JOIN bb_admin.bb_map_product_tag as maptag ON prod.product_id = maptag.product_id
LEFT JOIN bb_admin.bb_tags as tag ON maptag.tag_id = tag.tag_id
WHERE (prod.status='1' OR prod.status='3' OR prod.status='5')
AND prod.is_catalogue='1' AND cat.slug = 'barongs-suits'
AND tag.title IN ('gray','classic')
GROUP BY prod.product_id
What I want is to display products with tags 'gray' and 'classic'.
I tried AND tag.title = 'gray' AND tag.title = 'classic' but its not working well.
Thanks!
SELECT prod.*
FROM bb_admin.bb_directory_products as prod
LEFT JOIN bb_admin.bb_map_product_category as mapcat ON prod.product_id = mapcat.product_id
LEFT JOIN bb_admin.bb_categories_products as cat ON mapcat.category_id = cat.category_id
LEFT JOIN bb_admin.bb_map_product_tag as maptag ON prod.product_id = maptag.product_id
LEFT JOIN bb_admin.bb_tags as tag ON maptag.tag_id = tag.tag_id
WHERE (prod.status='1' OR prod.status='3' OR prod.status='5')
AND prod.is_catalogue='1' AND cat.slug = 'barongs-suits'
AND tag.title IN ('gray','classic')
GROUP BY prod.product_id
HAVING count(distinct tag.title) = 2
Add a having clause that makes sure a product_id has both tags.

Multiple tag search

I'm trying to make multiple tag search in my post tags.
So I have a table tags_posts (columns are id_tag and id_post).
If user types a few tag to input (I'll seperate and parse them using comma and array), sql query should return all posts that has all tags in user's input.
Here's my database table
What I've tried:
SELECT DISTINCT id_post, content, author_id, created, updated, username FROM tags_posts INNER JOIN posts ON posts.id=tags_posts.id_post INNER JOIN users ON users.id=posts.author_id WHERE id_tag IN (:tagids)
But in this case if any"one" of :tagids has in any post, that posts returns. But I'm looking for all tags for that post.
Try
SELECT *
FROM posts a JOIN
(
SELECT p.id
FROM tag_posts tp JOIN posts p
ON tp.post_id = p.id JOIN tags t
ON tp.tag_id = t.id
WHERE t.name IN ('tag1', 'tag2', 'tag3')
GROUP BY p.id
HAVING COUNT(DISTINCT t.id) = 3 -- << should the number of tags used in WHERE clause
) q ON a.id = q.id
The HAVING clause ensures that the query returns only posts that have all (three in example) tags.
Here is SQLFiddle demo
SELECT posts.id, posts.content, posts.created, posts.updated, posts.author_id, users.username
FROM posts
INNER JOIN users
on users.id = posts.author_id
INNER JOIN tags_posts
ON tags_posts.id_post = posts.id
INNER JOIN tags
ON tags.id = tags_posts.id_tag
WHERE tags.name = 'tag1'
AND tags.name = 'tag2'
AND tags.name = 'tag3'
Pass tag ids to be searched separated by comma in a string say $search and use the following query:
select id_post from tags_posts where id_tag IN ($search)
You could use the "IN" statement in MySQL for your parsed tags?
SELECT *
FROM Posts
LEFT JOIN Tag_Posts
ON Tag_Posts.Id_post = Posts.id
INNER JOIN Tags
ON Tags.id = Tag_Posts.Id_tag
WHERE Tags.name IN('PHP','SQL','LAMP')

Is there an alternative for MYSQL HAVING

I'm trying to find some records based on the tags they have. In order to find out which tags a record has i add them to the result using a subquery. To find out which results should be returned i added a having statement on the end of the query. But something tells me this is not the best way.
SELECT e.id, e.title, e.text, e.introduction,
UNIX_TIMESTAMP(e.starts_on) AS starts_on,
UNIX_TIMESTAMP(e.ends_on) AS ends_on,
m.id AS meta_id,
m.url,
cm.title AS category_title,
cm.url AS category_url,
CONCAT(
",",
(
SELECT GROUP_CONCAT(t.id)
FROM modules_tags AS mt
JOIN tags AS t ON t.id = mt.tag_id
WHERE mt.other_id = e.id
),","
) AS tags_search
FROM event_posts AS e
INNER JOIN meta AS m ON e.meta_id = m.id
LEFT JOIN event_categories AS c ON e.category_id = c.id
LEFT JOIN meta AS cm ON c.meta_id = cm.id
LEFT JOIN modules_tags AS mt ON mt.other_id = e.id
LEFT JOIN tags AS t ON t.id = mt.tag_id
WHERE 1 HAVING tags_search LIKE '%,5,%' AND tags_search LIKE '%,6,%'
FIND_IN_SET() is the best way to find values in comma separated values in MySQL.

Count number of articles in categories

This code only shows categories with articles in them.
I want to show all categories.
Pls help.
$query = "SELECT C.id, C.jcat_name,COUNT(A.catid) AS catid FROM jt_categories C INNER JOIN jt_articles A ON C.id = A.catid GROUP BY C.id";
change to left join
SELECT C.id, C.jcat_name,COUNT(A.catid) AS catid FROM jt_categories C
LEFT JOIN jt_articles A ON C.id = A.catid GROUP BY C.id
Change INNER JOIN for LEFT JOIN in your query.
INNER JOIN looks explicitely for the join in the data
replace the inner join by a left outer join
Change INNER JOIN to LEFT JOIN.
Did u try LEFT JOIN ? bc. (i think) in second table u have NULL articles for some categories.

Categories