Can I use a join query to do this? - mysql

I have to admit trying to understand JOINs makes my brain explode so I need some help.
What I'm trying to accomplish is return info on the last 25 postings in a forum, but the main posts table only returns numbers for the Topic and Forum, whereas I need the textual names of the topic and forum, which I can retrieve from two other tables. In my very limited understanding of joins, it seems I can use one to do all of this in a single query rather than coding 3+ queries with loops and other perhaps unneeded code.
This would be the main query:
SELECT post_id, topic_id, forum_id, post_time
FROM posts
ORDER BY post_id DESC
LIMIT 25
But for each of the 25 results I also want included forum_title from table forums where forum_id in that table matches the forum_id from the main query results, as well as topic_title from table topics where the topic_id in that table matches the topic_id in the main query results.
I'm hoping just even seeing what this would look like will help my understanding of how JOINs work.
Thanks
EDIT: I realized I should have used the exact column and table names so that I wouldn't be editing suggestions. Using the exact names, this is how Aquinas' suggestion would look:
SELECT post_id, topic_id, forum_id, post_time, Forum_Title, Topic_Title
FROM phpbb3_posts
INNER JOIN phpbb3_topics
on phpbb3_topics.topic_id = phpbb3_posts.topic_id
INNER JOIN phpbb3_forums
on phpbb3_forums.forum_id = phpbb3_posts.forum_id
ORDER BY post_id DESC
LIMIT 25
but I get this error (this is in mysql)
1052 - Column 'topic_id' in field list is ambiguous

SELECT post_id, topic_id, forum_id, post_time, Forum_Title, Topic_Title FROM posts
INNER JOIN topics on topics.topic_id = posts.topic_id
INNER JOIN forums on forums.forum_id = posts.forum_id
ORDER BY id DESC LIMIT 25

Related

PHP & MySQL Distinct results using INNER JOIN, WHERE and OR clauses (filtering duplicates)

I know the question is very old, but it's hard to run a very specific search and situations may still differ in little but crucial ways. I'm trying to figure out the correct mysql query for my forum search script. What I need is to list both topics and posts containing matching strings with a single query, both have to be supplied with respective results from another table (topics and posts). In case of any matches in the topic name there should be only 1 post per each topic, which is the very first one. My initial query thus:
SELECT topics.topic, posts.post
FROM topics
JOIN posts
ON
posts.topic_id=topics.id
WHERE topics.topic LIKE '%$search%'
OR posts.post LIKE '%$search%'
This will return all the posts with matching strings correctly but also a lot of duplicate topics along with as many irrelevant posts as the matching topic contains. Typically one should use GROUP BY in such situation, but it won't do any good here since grouping by either topics.topic or posts.post will be mutually detrimental. I also don't see how SELECT DISTINCT could help any and it doesn't seem to do anything anyway, and LIMIT 1 cannot be applied individually here. As always, my last resort is 2 individual queries, but if a single call is possible I would be very delighted to see it.
Ok everyone, the solution was lots more simple than expected (no sub-queries are necessary after all):
SELECT topics.topic, posts.post
FROM topics
JOIN posts ON posts.topic_id = topics.id
WHERE topic LIKE '%$search%'
GROUP BY topics.id
UNION
SELECT topics.topic, posts.post
FROM posts
JOIN topics ON topics.id = posts.topic_id
WHERE posts.post LIKE '%$search%'
Of course, the SELECT order matters a lot and one little thing can change everything. Thank you all for help and let's hope this will assist others in the future!
Try this:
SELECT t.topic as tpost, ppost = (SELECT post.post FROM posts p WHERE p.topic_id = t.id ORDER BY posts.id LIMIT 1)
FROM topics t
where topics.topic LIKE 'xxx'
union
SELECT t2.topic, p2.post
FROM posts p2
JOIN topics t2 on p2.topic_id = t2.id
WHERE posts.post LIKE 'xxx'
EDIT
Testing of this suggested solution proved a mod is required:
SELECT t.topic as tpost, (SELECT post.post FROM posts p WHERE p.topic_id = t.id ORDER BY posts.id LIMIT 1) as ppost
FROM topics t
where topics.topic LIKE 'xxx'
union
SELECT t2.topic, p2.post
FROM posts p2
JOIN topics t2 on p2.topic_id = t2.id
WHERE posts.post LIKE 'xxx'

How can I find records that contain multiple category IDs (using Toxi structure)?

SQL
SELECT *
FROM posts
LEFT JOIN taxonomy_term_map
ON (posts.ID = taxonomy_term_map.object_id)
WHERE taxonomy_term_map.term_id
IN (98,119)
GROUP BY posts.ID DESC
HAVING COUNT(posts.ID ) >= 2
LIMIT 0,20
TABLES & COLUMNS
posts { ID, post_title, etc... }
taxonomy_terms { term_id, term_label, term_slug, etc. }
post_taxonomy_term_map { map_id, object_id, taxonomy, term_id}
(NOTE: object_id relates to the posts.ID value)
My site uses the Toxi taxonomy structure for tagging/categorizing posts. Each post can have multiple term IDs attached to it.
Each taxonomy term associated with a post gets a record in the "post_taxonomy_term_map" table.
The query I'm current using returns matching records at the top of the results ("GROUP BY posts.ID DESC") along with additional records that don't fully match all the terms provided.
I only want to select records that match ALL of the term ID values provided, everything else should be ignored. Additionally, I want to order records by posts.rank, but I can't currently do that while ordering posts by GROUP.
I would appreciate some assistance.
I think this query will get what you need:
select p.* -- get only the columns from posts
from posts p
join post_taxonomy_term_map m on m.object_id = p.id
where m.term_id in (98, 119) -- filter to specific terms
group by p.id
having count(*) = 2 -- filter out incomplete ones
order by p.rank -- order by rank
limit 20 -- get only the first 20 rows

Select random record from mysql with multiple filters

I know this was discussed many times but my research did not help me with my problem.
I have a table (innodb) with about 3k records. I need to pick 1 row random with some filters, which i do it like this:
select id, title, topic_id
from posts
where id not in
(select post_id from records where user_id='$my_id' and checked='1')
and topic_id='$topic_id' and status='1'
order by RAND() limit 1
This gives me the result i wanted. The problem is this takes too much time even with 3k records. It will get slower when records are increased.
I have to find a solution for this. Any suggestions?
Update: Both tables are indexed with id columns.
Instead of using where id not in, I would use a LEFT JOIN:
SELECT id,
title,
topic_id
FROM posts p
LEFT JOIN records r
ON p.id = r.post_id
AND r.user_id='$my_id'
AND r.checked = '1'
WHERE p.topic_id='$topic_id'
AND status='1'
AND r.post_id IS NULL
ORDER BY RAND()
LIMIT 1;
With this, you will want an index on posts.id and another index on records.post_id, records.user_id, records.checked

Posts, comments and advanced sql filtering

I have a database table of posts. I have another db table with comments, with a column for comment_id (ai) and a column for the post_id it's attached to.
I want to make a query list all of my posts once, sorted by last posted comment.
The problem is I can't figure out the sql query to do it. Whatever I try I get all the comments listed too. I'm very skilled or experienced with mysql, but I tried using different "joins". Is there any way to do it?
SELECT posts.*, comments.last_one
FROM posts
LEFT JOIN
(SELECT MAX(updated) as last_one, post_id
FROM comments
GROUP BY post_id
) as comments
ON comments.post_id = posts.id
ORDER BY comments.last_one DESC
SELECT distinct p.* FROM posts as p, comments as c
WHERE p.post_id = c.post_id ORDER BY c.time DESC
I just assumed your relational schema. You might need to adapt the query.
Alternative: create a view with the last_comment_time as a field.
CREATE VIEW posts AS
(SELECT *, (SELECT time FROM comments WHERE post_id = p.post_id ORDER BY time DESC LIMIT 1) as last_comment_time
FROM posts as p);

How to write mysql subselect properly with conditions and limiting

I have three Tables:
Posts:
id, title, authorId, text
authors:
id, name, country
Comments:
id, authorId, text, postId
I want to run a mysql command which selects the first 5 posts which were written by authors, whose country is 'Ireland'. In the same call, I want to retrieve all the comments for those five posts, and also the author info.
I've tried the following:
SELECT posts.id as 'posts.id', posts.title as 'posts.title' (etc. etc. list all fields in three table)
FROM
(SELECT * FROM posts, authors WHERE authors.country = 'ireland' AND authors.id = posts.authorId LIMIT 0, 5 ) as posts
LEFT JOIN
comments ON comments.postId = posts.id,
authors
WHERE
authors.id = posts.authorId
I had to include every field with an alias ^ because there was a duplicate for id, and more fields in future may become duplicates as I'm looking for a generic solution.
My two questions are:
1) I am getting a duplicate field entry from within my subselect for id, so do I have to list out all my fields as aliases again within the subselect or is there only one field I need for a subselect
2) Is there a way to auto-alias my call? At the moment I've just aliased every field in the main select but can it do this for me so there are no duplicates?
Sorry if this isn't very clear it's a bit of a messy problem! Thanks.
You are doing an unnecessary join back to the author table in your query. You get all the fields you want in the posts subquery. I would rename this to something other than an existing table, perhaps pa to indicate posts and authors.
You say you want the first 5 posts, but have no order clause. A better form of the query is:
SELECT pa.id as 'posts.id', pa.title as 'posts.title' (etc. etc. list all fields in three table)
FROM (SELECT *
FROM posts join
authors
on authors.id = posts.authorId
WHERE authors.country = 'ireland'
order by post.date
LIMIT 0, 5
) pa LEFT JOIN
comments c
ON c.postId = pa.id
Note that this returns the first five posts and their authors (as specified in the question). But one author may be responsible for all five posts.
In MySQL, you can use * and it will get rid of duplicate aliases in the from clause. I think this is dangerous. It is better to list all the columns you want.
To answer your questions:
You can select as many (or as few) columns as you need from a sub-query
You do not need to join the authors table again since you already selected all fields in the sub-query (and so get rid of duplicate columns names).
A few additional remarks...
... about the JOIN syntax
Prefer the form
FROM t1 JOIN t2 ON (t1.fk = t2.pk)
to the obsolete, obscure
FROM t1, t2 WHERE t1.fk = t2.pk
... about the use of a LIMIT clause without an ORDER BY clause
The order in which rows are returned by a SELECT statement without an ORDER BY clause is undefined. Therefore, a LIMIT n clause without an ORDER BY clause could return any n rows in theory.
Your final query should look like this:
SELECT *
FROM (
SELECT *
FROM posts
JOIN authors ON (authors.id = posts.authorId )
WHERE authors.country = 'ireland'
ORDER BY posts.id DESC -- assuming this column is monotonically increasing
LIMIT 5
) AS last_posts
LEFT JOIN comments ON ( comments.postId = last_posts .id )