Selecting data based on related table in MySQL - php

I have the following tables:
posts
---------------------
id | name
---------------------
1 | First Post
2 | Another post
tags
---------------------
id | name
---------------------
1 | Tag1
2 | Tag2
3 | Tag3
posts_tags
---------------------
post_id | tag_id
---------------------
1 | 2
1 | 3
2 | 1
3 | 2
I'm trying to write a query that (in human readable terms) would say something like 'Find all of the posts which are tagged with tag1 or tag2'.
So I can put multiple tag IDs in and get a lists of posts that have at least one of those tags back.
I've tried this:
SElECT t1.name
FROM posts AS t1 INNER JOIN posts_tags AS t2
ON t1.id = t2.post_id
WHERE t2.tag_id IN(1,2)
But this returns no results. I'm not really sure where to go from here. Is there any way I can do this in one query - i.e without first fetching the posts, then looping through them to fetch the tags?

Use the IN operator:
SELECT post_id
FROM posts_tags
WHERE tag_id IN (1,2,3)
To avoid getting post_id duplicates use the DISTINCT:
SELECT DISTINCT post_id
...

SELECT
t1.post_id
FROM
posts_tags as t1
INNER JOIN
posts AS t2
ON
t1.post_id = t2.id
WHERE
t2.tag_id IN(1,2)
By Joining the Posts table you now have access to all of the post data, which I assume is what you are looking for.

you have to join posts_tags table also.
SElECT t1.name FROM posts as t1 INNER JOIN posts_tags as t3 on t3.post_id = t1.id INNER JOIN tags AS t2 ON t2.id = t3.tag_id WHERE t2.id IN(1,2)
SQLFiddle answer here.
http://www.sqlfiddle.com/#!2/d785e/5

SELECT posts.name FROM posts LEFT JOIN post_tags ON posts.id = post_tags.post_id WHERE post_tags.tag_id = '1' OR post_tags.tag_id = '2'

Related

Display 0 results in the left join query

How do I count the number of participants in the event_participants table and display also the event_title with zero results. This is my table
event_title
id | name
1 | New York
2 | Canada
event_participants
id | event_title_id | name
1 | 1 | Jon
2 | 1 | Mike
This is my query
SELECT count( * ) AS count
FROM event_participants AS t1
LEFT JOIN event_title AS t2 ON t1.event_title_id = t2.id
GROUP BY t1.event_title_id
I am getting a correct result but does not display the 0 results.
I should be getting a result like this
New York | 2
Canada | 0
In place of * use t2.id it will work
SELECT count(t2.id) AS count
FROM event_participants AS t1
LEFT JOIN event_title AS t2 ON t1.event_title_id = t2.id
GROUP BY t1.event_title_id
Your group by is the problem.
you are grouping by the event_title_id where you dont have the id 2.
What you should do is a group by the name of the first table.
SELECT count( * ) AS count
FROM event_participants AS t1
LEFT JOIN event_title AS t2 ON t1.event_title_id = t2.id
GROUP BY t2.name
should work
Change your query like below to get desired output:
SELECT count(t2.id) AS count
FROM event_title AS t1
LEFT JOIN event_participants AS t2 ON t2.event_title_id = t1.id
GROUP BY t1.id
//To retrieve title also
SELECT t1.title,count(t2.id) AS count
FROM event_title AS t1
LEFT JOIN event_participants AS t2 ON t2.event_title_id = t1.id
GROUP BY t1.id

SQL select posts that have similar tags

In my MariaDB I have a table called
"Posts" with
id, title, username_id, text, image_url, url
one called "tags" with
id, tag
and one called post_tags with
id, post_id, tag_id.
The thing I want to accomplish is to get 3 posts from the "Posts" table that have the most tags in common with the post that's currently shown on the page.
I'm stuck here not knowing where to start.
EDIT
Posts
id | username_id | title | text | image_url | url
1 1 example example_text localhost/image.jpg localhost/first-post
2 1 example1 example_text localhost/image1.jpg localhost/second-post
3 1 example2 example_text localhost/image2.jpg localhost/third-post
4 1 example4 example_text localhost/image4.jpg localhost/fourth-post
... ... ... ... ... ...
... ... ... ... ... ...
Tags
id | tag
1 herbs
2 flower
3 rose
Post_tags
id | post_id | tag_id
1 1 1
2 1 2
3 1 3
4 2 1
5 3 1
6 3 2
7 4 1
8 4 2
9 4 3
I'd like to return an array with posts.title and posts.image_url selecting the posts that have the most post_tags.tag_id in common with the current one.
As you can see, if we take post n. 1 as the selected post, post n. 4 has the most tags in common with it, post n.3 is in the second position and post n.2 is the third.
example4 | localhost/image4.jpg
example3 | localhost/image3.jpg
example2 | localhost/image2.jpg
I hope I made it clearer. Thank you.
SELECT p.id, p.title, p.image_url, COUNT(*) as how_many_shared_tags
FROM posts p
JOIN post_tags pt ON pt.post_id = p.id
AND pt.tag_id IN(SELECT tag_id FROM post_tags WHERE post_id = 1)
WHERE p.id != 1
GROUP BY p.id, p.title, p.image_url
order by COUNT(*) DESC
LIMIT 3
As requested, explanation for query:
In order to find top 3 posts that share most tags with our "parent" post, we first need to get list of tags that "parent" has => SELECT tag_id FROM post_tags WHERE post_id = 1
Then find posts that have at least one of those tags by searching in table that holds both ID for posts and tags by adding condition tag_id IN(LIST_OF_tag_id_FROM_SUB_SELECT_SHOWN_ABOVE).
Now we know what posts share at least one tag with "parent", so we can count how many tags they have actually in common and sort by it => order by COUNT(*) DESC
Because "parent" post also "shares" those tags and we don't want him in our result, we give additional condition excluding ID of our "parent" => WHERE p.id != 1
Finally we limit result set to 3 rows, because we want only top 3. LIMIT 3
Selecting count is not required, it is only to point out what it counts COUNT(*) as how_many_common_tags
Check this if it gives what you want to get. Then you may need to optimize query.
SELECT
t1.*
FROM posts t1,
(
SELECT
post_id
FROM post_tags t2
WHERE
tag_id IN (SELECT tag_id FROM post_tags WHERE post_id = $CURRENT_POST_ID)
AND NOT post_id = $CURRENT_POST_ID
GROUP BY post_id
ORDER BY COUNT(tag_id) DESC
LIMIT 3
) t2
WHERE
t1.id = t2.post_id
This will get you just the post_id values:
SELECT x.post_id
FROM
(
SELECT b.post_id
FROM Post_tags a
JOIN Post_tags b USING(tag_id)
WHERE a.post_id = 1234
AND b.post_id != a.post_id
) x
GROUP BY x.post_id
ORDER BY COUNT(*) DESC
LIMIT 3;
Tips on designing a better mapping table (Post_tags). That will give you the optimal indexes for that table.
To get more info on the 3 posts:
SELECT p.*
FROM
(
SELECT x.post_id
FROM
(
SELECT b.post_id
FROM Post_tag a
JOIN Post_tag b USING(tag_id)
WHERE a.post_id = 1234
AND b.post_id != a.post_id
) AS x
GROUP BY x.post_id
ORDER BY COUNT(*) DESC
LIMIT 3
) AS y
JOIN Posts AS p ON p.id = y.post_id;

MYSQL : select count with union three table

I have three table content information about posts
and each table has Post_id it's foreign_key for Post table
first table = `likes`
second table = `Comment`
and last one = `Visitor`
each Table has some info about users like session or id and etc
i need to create new view table contain post id and the number of visitor , likes , comment
i tried this
SELECT *
from (
select id , count(id) as Comment
from Post left join Comment on id = Post_id
group by id
UNION
select id, count(id) as Visitor
from Post left join Visitor on id = Post_id
group by id
UNION
select id, count(id) as Likes
from Post left join Likes on id = Post_id
group by id
) CountsTable
GROUP BY CountsTable.id
but it didnt work .
i dont know why the result is only the first inner select
in my example the result is
| id | Comment|
|--------|------- |
| 1 | 55 |
| 2 | 25 |
| 3 | 12 |
i expect something like that
| id | Comment | Likes | Visitor |
|--------------|-------|---------|
| 1 | 55 | 100 | 2000 |
No need to use UNION. Count the records for each post in all three tables and Left Join result with Post table
Try something like this
SELECT id,
comments,
vistors,
likes
FROM Post p
LEFT JOIN (SELECT Count(Post_id) comments, Post_id
FROM Comment
GROUP BY Post_id) c
ON p.id = c.Post_id
LEFT JOIN (SELECT Count(Post_id) vistors, Post_id
FROM Visitor
GROUP BY Post_id) v
ON p.id = v.Post_id
LEFT JOIN (SELECT Count(Post_id) likes, Post_id
FROM Likes
GROUP BY Post_id) l
ON p.id = l.Post_id
You can do it with a Left Join query, e.g.:
select u.id, count(distinct l.id) as likes, count(distinct c.id) as comments
from user u left join likes l on u.id = l.user_id
left join comments c on u.id = c.user_id
group by u.id;
Here is SQL Fiddle.
SELECT id, SUM(Comment),SUM(Visitor),SUM(Likes) from (
select id , count(id) as Comment, 0 as Visitor, 0 as Likes
from Post left join Comment on id = Post_id
group by id
UNION ALL
select id, 0 as Comment, count(id) as Visitor , 0 as Likes
from Post left join Visitor on id = Post_id
group by id
UNION ALL
select id, 0 as Comment, 0 as Visitor, count(id) as Likes
from Post left join Likes on id = Post_id
group by id
) CountsTable
GROUP BY CountsTable.id

how to get data from both table with join

Table 1 : Main_Family_Member
ID | Name
1 | Mahesh
2 | Rahul
3 | Jay
Table 2 : Family_Members
ID | MainMember | Name
1 | 1 | 'Arun'
2 | 1 | 'Nitin'
3 | 2 | 'Pratik'
Want Result :
Name
Mahesh
Arun
Nitin
Rahul
Pratik
You can achieve this by doing a UNION ALL of the two tables along with proper ordering. Note that it is necessary to union the two tables joined, because we need to know whether a main family member has any members. In case he does not have any member, your sample output implies that you don't want to display that main family member at all.
SELECT t.Name
FROM
(
SELECT DISTINCT t1.ID, t1.Name, 0 AS position
FROM
(
SELECT t1.ID, t1.Name
FROM Main_Family_Member t1
INNER JOIN Family_Members t2
ON t1.ID = t2.MainMember
WHERE t2.ID IS NOT NULL
) t1
UNION ALL
SELECT t2.ID, t2.Name, 1 AS position
FROM
(
SELECT t2.MainMember AS ID, t2.Name
FROM Main_Family_Member t1
INNER JOIN Family_Members t2
ON t1.ID = t2.MainMember
WHERE t2.ID IS NOT NULL
) t2
ORDER BY ID, position, Name
) t
Demo here:
SQLFiddle
SELECT Main_Family_Member.Name, Family_Members.Name
FROM Main_Family_Member
INNER JOIN Family_Members
ON Main_Family_Member.ID = Main_Family_Member.MainMember;
SELECT Main_Family_Member.Name, Family_Members.Name
FROM Main_Family_Member
INNER JOIN Family_Members
ON Main_Family_Member.ID = Main_Family_Member.MainMember;
You will need to perform a INNER JOIN on the two tables. This would return all the rows in the first table that meet the conditions plus all rows on the second table that join with the first table on the unique fields.
SELECT Main_Family_Member.Name , Family_Members.Name FROM Main_Family_Member INNER JOIN Family_Members ON Main_Family_Member.ID = Family_Members.MainMember WHERE Main_Family_Member.ID = 1 OR Main_Family_Member.ID = 2
SELECT Main_Family_Member.Name,Family_Members.Name FROM Main_Family_Member
INNER JOIN Family_Members ON Main_Family_Member.ID=Family_Members.MainMember

3 tables, left join, in case DATA is missing in ONE table

table1 (ids always exist)
+----+------+
| id | col1 |
+----+------+
| 1 | ab |
+----+------+
| 2 | gh |
+----+------+
table2 (ids always exist)
+----+------+
| id | col2 |
+----+------+
| 1 | cd |
+----+------+
| 2 | ij |
+----+------+
table3 (ids might be missing, in this case 2 is missing)
+----+------+
| id | col3 |
+----+------+
| 1 | ef |
+----+------+
PHP
$col = 'ab';
$a = mysql_query("SELECT t1.id FROM table1 AS t1, table2 AS t2, table3 AS t3
WHERE t1.id = t2.id AND t2.id = t3.id AND (t1.col1 = '$col' OR t2.col2 = '$col'
OR t3.col3 = '$col) GROUP BY t1.id, t2.id, t3.id");
That would only work if all three tables had "the same id" included, but what happens if an "id" is missing in table3 for whatever reason? how can I still test for all three tables and get t1.id to output 1, when $col = ab? would I have to use left join?
$a = mysql_query("SELECT t1.id FROM table1 AS t1, table2 AS t2
LEFT JOIN (SELECT id FROM table3 WHERE col3 = '$col') AS t3 ON t3.id = t1.id
WHERE t1.id = t2.id AND (t1.col1 = '$col' OR t2.col2 = '$col')
GROUP BY t1.id, t2.id");
what am I doing wrong here?
What are you doing wrong? Querying a table that doesn't exist. That's always going to raise an error.
I'm not going to address the wisdom of designing a database in which tables crucial to your queries come and go.
Your only hope on the client side is to
test for the existence of the tables
you're interested in, and
execute different SQL statements
based on those results.
[After your edit]
It sounds like you need either one or two left outer joins. This gives you all the ids that are common to both table1 and table2, regardless of whether they're in table3.
select t1.id, t2.id, t3.id
from table1 t1
inner join table2 t2 on (t1.id = t2.id)
left join table3 t3 on (t1.id = t3.id);
And this gives you all the ids that are in table1, regardless of whether they're in table2 or table 3.
select t1.id, t2.id, t3.id
from table1 t1
left join table2 t2 on (t1.id = t2.id)
left join table3 t3 on (t1.id = t3.id);
And, of course, you can filter the results with your WHERE clause.
LEFT JOIN only works when the table DOES exist, but contains no data.
If you don't want to have to do stuff like Catcall suggested (check if the table exists, and use different SQL statements based on that...) you have to make sure that the table exists in the database, even if it's completely empty.
Under normal circumstances, you should have a certain degree of control over your application's database, so you should be able to make sure that the table is really there.
If this is really a big problem (like, that you can't be sure if someone deleted the table) you could check this every time you start your application: create the table if it doesn't exist.

Categories