Using a second table as a lookup? - mysql

I have Three tables,
Posts,
Tags,
Posts_Tags_Link
Posts has:
id, content
Tags has: id, tag
Posts_Tags_Link has: post_id, tag_id
Basically if a tag is linked to a post then an entry is created in Posts_Tags_Link as this is a many-many relationship.
Anyway, I want to do some searches and return all rows from Posts that are linked to a particular keyword.
E.g. If I have the
Posts:
id | content
1 | some stuff
2 | more stuff
3 | stuff again
Tags:
id | tag
1 | first
2 | second
3 | third
4 | fourth
Posts_Tags_Link
post_id | tag_id
1 | 1
1 | 2
2 | 2
3 | 3
3 | 4
and I search for second I want to return
id | content
1 | some stuff
2 | more stuff
I assume I am to use a join for this,
Would I just join my posts table to the link table, on the post_id and join the link table to the link table to the tags table on the tag_id column?
I believe that is right, but If I only want to rows that match the search (like not where) would I use like or would one of the different joins work?
I want that if I search for sec it would have the same result as if I searched for second so believe that I have to do this using like?

You should join the three tables since you want to search from them, example
SELECT a.*
FROM post a
INNER JOIN Posts_Tags_Link b
on a.id = b.post_id
INNER JOIN Tag c
ON b.tag_tag_id = id
WHERE a.content like '%keyword%' OR -- build you conditions here
c.tag like '%keyword%'

Try to use the following query.
SELECT p.id, p.content FROM
Posts_Tags_Link ptl
INNER JOIN Posts p ON p.id = ptl.post_id
INNER JOIN Tags t ON t.id = ptl.tag_id
WHERE t.tag = 'second'

Related

Unable to write a proper query to join two tables and fetch what I need

I'm trying to fetch data from table1 which doesn't have a column with a specific value I include
Consider a social media site:
I have a "posts" table where I save all the posts by the user with their User id,
and I have the "follow" table where I save all the data like who's following who.
Now I'm trying to get all the data from the posts table where the user isn't following them
Example:
posts table
| u_id | Post |
|:---- |:----:|
| 1 |post1 |
| 2 |post2 |
| 1 |post3 |
| 3 |post4 |
follow table:
| u_id | following |
|:---- |:---------:|
| 2 | 1 |
| 1 | 3 |
Now the scenario: Let's say I'm the logged-in user with user id: 2, as per the requirement I should only see the posts of users that I'm not following, i.e., user #2 (which is me) and user #3 only.
So far the query I tried is:
$query = "SELECT posts.id, posts.u_id, posts.title, posts.date
FROM posts
LEFT JOIN follows
ON posts.u_id=follows.u_id
WHERE (follows.u_id != '$u_id')";
The $u_id is the logged-in user's id which is in the above scenario #2.
Now I m trying to get all the data from the posts table where the user isn't following them
SELECT p.id, p.u_id, p.title, p.date
FROM posts p LEFT JOIN
follows f
ON p.u_id = f.following AND
f.u_id = ?
WHERE u.u_id IS NULL;
LEFT JOIN is a fine approach. However, this is looking at what posts match following not u_id. Then you want to return the rows where there are no matches.
You ask to find posts of users which you are not following. Use NOT IN, NOT EXISTS, or LEFT JOIN:
SELECT *
FROM posts
WHERE posts.u_id NOT IN (SELECT following FROM follows WHERE u_id = 'me')
AND posts.u_id <> 'me' -- Remove this line if you want to see your posts.
;
I assume you wouldn't follow yourself. Just remove the noted line in the WHERE clause to see your posts.
This is similar to the LEFT JOIN approach.

Entry with most matching relations

I'm trying to create little "recommended" functionality based on the posts with the most matching tags.
I got a layout like this:
Posts
id
---
1
2
3
4
post_tags
post_id | tag_id
---------+---------
1 | 1
1 | 2
2 | 2
2 | 3
2 | 4
3 | 1
3 | 2
3 | 4
4 | 5
tags
id
----
1
2
3
4
5
So if I would retrieve recommendations for the post with id 1 the list should go
3 (2/2 matches)
2 (1/2 matches)
4 (0/2 matches)
My Query so far looks like this:
SELECT DISTINCT
p.id,
p.title,
count(*) as cnt
FROM
posts p
INNER JOIN posts_tags pt ON pt.post_id= p.id
INNER JOIN tags t ON pt.tag_id = t.id
WHERE
t.id IN (
SELECT
pt.tag_id
FROM
posts_tags pt
WHERE
pt.post_id = '30213'
)
GROUP BY
t. NAME
order by count(*) desc
LIMIT 0, 4
I know DISTINCT isn't working because of the count but I wanted to see just what he counted, so the result looks like this:
4 Foo 4881
4 Foo 2560
11 Bar 2094
12 Baz 1998
So what happened? It counted the occurences of the tag in general. So appearantly the first associated tag of "Post 1" is 4881 associated and then pulls the first entry that matches... the one with the lowest id.
I see the problem but I can't solve it.
Your group by makes no sense. You want to aggregate by the post not the tag:
SELECT p.id, p.title, count(*) as cnt
FROM posts p INNER JOIN
posts_tags pt
ON pt.post_id = p.id
WHERE pt.tag_id IN (SELECT pt2.tag_id
FROM posts_tags pt2
WHERE pt2.post_id = 30213
)
GROUP BY p.id, p.title
ORDER BY count(*) desc
LIMIT 0, 4;
This will not return 0. If that is important, you need to use a LEFT JOIN instead of WHERE . . . IN . . ..
Also:
SELECT DISTINCT is almost never used with GROUP BY. It is hard (but not impossible) to come up with a use-case for it.
You don't need the tags table, so I removed it.
Don't use single quotes around numbers. I am guessing that post_id is really a number.
The fix is in the GROUP BY.

MYSQL Optimising JOINED UNION SELECT query

I've re-written this query a few times now (it's Monday) with the attempt of finding the most efficient way of getting the data I require however I'm not sure I'm even approaching it correctly at the moment.
To summarise the problem;
Users have two sets of tags (key_terms, project_terms), there's a link table between each of these between users and tags tables.
I would like to pull out any users that have specified tags in either table. Ideally it'd also include the 'most relevant' tag to that user - but lets put that aside for now.
users
| id | name |
| 1 | dayjo |
| 2 | stackoverflow |
tags
| id | tag |
| 1 | tag1 |
| 2 | tag2 |
user_key_term
| user_id | tag_id |
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
project_key_term
| user_id | tag_id |
| 1 | 3 |
| 2 | 3 |
What I want to be able to query on is the named tags, i.e if I search for "tag1" both users should be returned, however if I search for "tag2" only User 1 should be returned.
My Solutions
I tried by selecting users, and joining tags twice (one for each link table), this seemed to work ok but wasn't sure it was the best way, and couldn't figure out how to get the most relevant tag.
SELECT t1.tag, t2.tag as most_relevant_tag, users.* FROM users
LEFT JOIN user_key_term ON user_key_term.user_id = users.id
LEFT JOIN tags t1 ON user_key_term.tag_id = t1.id
LEFT JOIN project_key_term ON project_key_term.user_id = users.id
LEFT JOIN tags t2 ON project_key_term.tag_id = t2.id
WHERE t1.tag IN ('tag1','tag2') OR t2.tag IN ('tag1','tag2')
GROUP BY users.id;
My next attempt was a UNION select, but this one feels dirty;
SELECT users.* FROM
`users`
INNER JOIN (
SELECT project_key_term.user_id, tags.id, tags.tag FROM project_key_term
JOIN tags ON tags.id = project_key_term.tag_id AND tags.tag IN ('tag1')
UNION ALL
SELECT user_key_term.user_id,tags.id, tags.tag FROM user_key_term
JOIN tags ON tags.id = user_key_term.tag_id AND tags.tag IN ('tag1')
) tags ON tags.user_id = users.id
WHERE tags.tag IN ('tag1')
GROUP BY users.id;
But
I've tried running EXPLAIN on both queries to see which is best, but it doesn't reveal anything particularly useful to me. Especially because at the moment there's not a lot of data in the tables, there will potentially be hundreds / thousands of tags.
Any help on the 'correct' or best practice way to do this sort of query would be great!
The union query can be simplified to:
SELECT users.*
FROM users
INNER JOIN (
SELECT user_id,tag_id
FROM project_key_term
UNION ALL
SELECT user_id,tag_id
FROM user_key_term
) alltags ON alltags.user_id = users.id
INNER JOIN tags t on t.id = alltags.tag_id
where t.tag IN ('tag1')
Edit: Getting the most relevant tags
SELECT score, t.tag, users.*
FROM users
INNER JOIN (select user_id, tag_id, count(*) as score
from (SELECT user_id,tag_id
FROM project_key_term
UNION ALL
SELECT user_id,tag_id
FROM user_key_term
) alltags
group by user_id,tag_id) tagcounts ON tagcounts.user_id = users.id
INNER JOIN tags t on t.id = tagcounts.tag_id
where t.tag IN ('tag1','tag2','tag3')
ORDER BY score DESC

How to do multiple selects in one query?

I have 2 tbl, author and posts.
+---------------+
| AUTHOR |
+---------------+
id | name
+-----------------+
| POST |
+-----------------+
|id |title|content|
i want to have the results of select * from author and select * from posts side by side
+------------------------------+
| RESULT |
+------------------------------+
id | name | id | title | content
how to do that? And how to do that for more than 2 columns without using a FK.
OBS: Im using MYSQL
Use join:
SELECT columns FROM Author INNER JOIN Result ON Author.id = Result.id;
Hopefully the id in both tables is the relation.
As we can see there is no relation between the tables, so one solution could be:
select * from author, posts
And it'll create a matrix
you can select two table comumns like this
SELECT authoer.id,authoer.name,posts.id,posts.name,posts.title,post.content from author,posts
if you have relation between two tables your query like this
select authoer.id,authoer.name,posts.id,posts.name,posts.title,post.content from author,posts
where authoer.id=posts.id
you also use JOIN to
try this:
select a.id, a.name, p.id, p.title, p.content
FROM author a
INNER JOIN post p ON a.id = p.id;
The INNER JOIN keyword selects all rows from both tables as long as there is a match between the columns in both tables.
TRY THIS:
SELECT A.ID,A.NAME,B.ID,B.TITLE,B.CONTENT
FROM POST A
INNER JOIN AUTHOR B
ON A.ID=B.ID

Select one row from this table and all related rows from other table

table user
id | name | address
3 | Jacko | 33A Herewini
table user_photo
id | userid | thumb | full
1 | 3 | 3k1j_thumb.jpg| 3k1j.jpg
1 | 3 | 3k1j_thumb.jpg| 3k1j.jpg
2 | 14 | 44r_thumb.jpg| 44r.jpg
2 | 14 | 55t_thumb.jpg| 55t.jpg
2 | 14 | 12f_thumb.jpg| 12f.jpg
I got the user id, I want to select his name and address and all his photos
PS: what tool/software you use to draw the table line (the +---+) ?
Edit: then how would you put the name in a div and all the photos in a ul
my html look like this
<div class='name'></div>
...600 elements...
<ul class='photos'></ul>
To get the address, you can just query that table:
SELECT * FROM user WHERE id = 3
To get the photos, you can query the user photo table:
SELECT * FROM user_photo WHERE userid = 3
If you want to get everything at once, you can join the two tables together on the user id:
SELECT * FROM user u
LEFT JOIN user_photo up ON up.userid = u.id
WHERE u.id = 3
Note though that the address info will of course be duplicated on every row
Learn about LEFT JOIN, see e.g. http://www.postgresql.org/docs/8.2/static/sql-select.html
SELECT * FROM user AS u LEFT JOIN user_photo AS p ON p.userid = u.id
Adjust as needed.
To combine the results of two tables linked by one column (id in this case), you would use a JOIN.
You can read up on JOINs in the MySQL Reference Manual
SELECT *
FROM user u
LEFT OUTER JOIN user_photo up ON up.userid = u.id