I'm fairly new to both mysql and php so I am still getting my head around it all, so please bear with me.
I basically have a site where users can make topics, and tag tagwords to their topics, I am trying to join the tables so when I query for the POSTS, it can also show the tag information in there.
"SELECT u.user_id, u.username, u.profile, topic_tags.tag_id, tags.tag_id, tags.tags,
p.post_id, p.post_content, p.post_date, p.post_topic, p.post_by, p.invisipost
FROM `posts` p
JOIN `users` u on p.post_by = u.user_id
JOIN `topics` t on p.post_topic = t.topic_id
WHERE p.post_topic='$id'
INNER JOIN `tags` ON topic_tags.tag_id = tags.tag_id
INNER JOIN `topic_tags` ON topics.topic_id = topic_tag.tag_id
WHERE topic_tags.tag_id = topics.topic_id";
Like I said I am still very new to this so if you could offer any advice I would be much appreciative.
EDIT: here is the code that calls the tags
<?php
$topic_id = $rows['topic_id'];
$sql=mysql_query("SELECT * FROM topic_tags WHERE `topic_id`='{$topic_id}'");
while($rowd=mysql_fetch_array($sql))
{
$tag_id = $rowd['tag_id'];
$fetch_name = mysql_fetch_object(mysql_query("SELECT * FROM `tags` WHERE `tag_id`='{$tag_id}'"));
?>
<div id="topic-tagged"><?php echo ucwords($fetch_name->tags);?></div>
<?php
}
?>
All the wheres should be at the end:
SELECT u.user_id, u.username, u.profile,
topic_tags.tag_id, tags.tag_id, tags.tags,
p.post_id, p.post_content, p.post_date,
p.post_topic, p.post_by, p.invisipost
FROM `posts` p
JOIN `users` u on p.post_by = u.user_id
JOIN `topics` t on p.post_topic = t.topic_id
INNER JOIN `tags` ON topic_tags.tag_id = tags.tag_id
INNER JOIN `topic_tags` ON topics.topic_id = topic_tag.tag_id
WHERE p.post_topic='$id' and topic_tags.tag_id = topics.topic_id
This is the corrected query statement based on your original question. I am still not sure if the last part is correct though. You might want to run this in your database directly and see if you get the results you need.
There are a few rules when writing SQL. The where clause comes after the from clause. In addition, tables can only be references in an on clause after they have been placed in the from clause. And, a query only has one from statement and one where statement. All joins are placed in the from statement.
Two good practices are to use table aliases that are abbreviations for the table (which you do sometimes). And, don't mix join and inner join. They are synonyms, but only one should be used in a query.
SELECT u.user_id, u.username, u.profile, tt.tag_id, ta.tag_id, ta.tags,
p.post_id, p.post_content, p.post_date, p.post_topic, p.post_by, p.invisipost
FROM `posts` p join
`users` u
on p.post_by = u.user_id join
`topic_tags` tt
ON p.post_topic = tt.topic_id join
`tags` ta
ON tt.tag_id = ta.tag_id
WHERE p.post_topic='$id';
Finally, I'm pretty sure that you do not want and tt.tag_id = topics.topic_id. This is comparing a tag_id to a topic_id. They are not referring to the same thing. I think the joins as shown above are sufficient for your query.
Related
I am stuck with some SQL query.
I have four tables. Which are connected:
user =>user_account=>acount_profile_entries=>profile_entries
From left to right they are one to many.
user_account has a user_id field as FK.
account_profile_field has user_account_id and profile_entry_id.
Profile_entries has a text field that I need to show for each user (account).
I need to write a query that will show me, all accounts for every user, and its profile entries.
I am sorry if this is confusing, I tried to make it simple
This is what I have done so far. I can show all accounts for every user and this is the point I am stuck with. Last two commented out Joins are not working properly. I believe I am close somewhat, I just need a push :)
SELECT
u.email AS Email,
u.id AS UserId,
ua.id AS UserAccountId,
ua.app_id AS Application
FROM users AS u
INNER JOIN user_accounts ua ON ua.user_id = u.id
-- INNER JOIN account_profile_entries ape ON ape.user_account_id = ua.id
-- INNER JOIN profile_entries as pe ON pe.id = ape.profile_entry_id
limit 10
Try this SQL Query with using LEFT JOIN
Description :- The MySQL LEFT JOIN joins two tables and fetches rows based on a condition, which is matching in both the tables and the unmatched rows will also be available from the table written before the JOIN clause.
SYNTAX
SELECT column_name(s)
FROM table1
LEFT JOIN table2 ON table1.column_name = table2.column_name;
SELECT u.*,
u.id AS UserId,
ua.id AS UserAccountId,
ua.app_id AS Application,pe.* FROM `users` u
LEFT JOIN user_accounts ua ON ua.user_id = u.id
LEFT JOIN account_profile_entries ape ON ape.user_account_id = ua.id
LEFT JOIN profile_entries as pe ON pe.id = ape.profile_entry_id LIMIT 10
Please assume this queries:
SELECT p.* FROM posts p
JOIN posts_tags pt ON pt.post_id = p.id
JOIN tags t ON pt.tag_id = t.id AND t.name = 'php'
SELECT p.* FROM posts p
JOIN posts_tags pt ON pt.post_id = p.id
JOIN tags t ON pt.tag_id = t.id
WHERE t.name = 'php'
AS you know, both have an identical result. But this condition t.name = 'php' is in JOIN clause in the first query and it is on the WHERE clause on the second query. I want to know which one is better and why?
Generally, adding condition in Where clause, makes the code more clearer. When you add them to the AND clause, it gives a feeling that JOIN is based on the combination of two fields.
Adding condition to Where clause, might help the optimizer to filter out the records even before joining, in case of large tables. I would suggest to keep it in the WHERE clause.
EDIT
Also, refer to this related post: Join best practices
I have 5 tables I want to select data from, but sometimes the 5th table (named comment) will be empty. When that happens I want my query to return null for the values from that table, and only return the other values (the 5th table includes user comments, sometimes there are none).
Here is my query:
SELECT articles.title, articles.posted, articles.body, authors.name, authors.img, authors.bio, comment.user_id, comment.text, comment.article_id, GROUP_CONCAT(categories.cat_name) AS cat_name
FROM articles, authors, categories, article_categories, comment
WHERE articles.author_id = authors.id
AND articles.id = article_categories.article_id
AND article_categories.category_id = categories.id
AND articles.id = comment.article_id
AND title LIKE :title;
the :title comes from PDO and is not relevant here.
The problem comes from the AND articles.id = comment.article_id. I don't know how to write it in a way to only check for this, if it's there and ignore it otherwise.
Thanks for the help
You should use proper join syntax, and in this case instead of INNER JOIN use LEFT JOIN for comment table:
SELECT articles.title, articles.posted, articles.body, authors.name, authors.img, authors.bio, comment.user_id, comment.text, comment.article_id, GROUP_CONCAT(categories.cat_name) AS cat_name
FROM articles INNER JOIN authors ON articles.author_id = authors.id
INNER JOIN article_categories ON articles.id = article_categories.article_id
INNER JOIN categories ON article_categories.category_id = categories.id
LEFT JOIN comment ON articles.id = comment.article_id
WHERE title LIKE :title;
You should use left/right joins to do that.
[...]
select *
from articles a
left join comments c
on a.id = c.article_id
[...]
To learn more about joins:
http://blog.codinghorror.com/a-visual-explanation-of-sql-joins
I use this query to select all articles :
SELECT articles.*,categories.category_name,users.username,tags.tag
FROM articles
LEFT JOIN `categories` ON articles.category_id = categories.category_id
LEFT JOIN `users` ON articles.author_id = users.user_id
LEFT JOIN `tags` ON articles.article_id = tags.article_id
ORDER BY articles.date_added DESC
I have an other table comments, and I want to count how many comments are there, where the article_id in that table = article_id in the articles table. I tried with COUNT, but then it returns only one result. How can I do that with one query?
You can use a subquery in the SELECT clause:
SELECT articles.*,categories.category_name,users.username,tags.tag, (SELECT count(*) FROM comments c WHERE c.article_id = articles.article_id) as comments_count
As arnaud576875 already stated, you can use a subquery to extract the summary data.
Two things I've noticed from your SQL that are not really a part of the question but still worth pointing out.
you can use a table alias to shorten your SQL and make it more readable.
So instead of
SELECT articles.*,categories.category_name,users.username,tags.tag
FROM articles
LEFT JOIN `categories` ON articles.category_id = categories.category_id
LEFT JOIN `users` ON articles.author_id = users.user_id
LEFT JOIN `tags` ON articles.article_id = tags.article_id
ORDER BY articles.date_added DESC
you'd code
SELECT a.*, c.category_name, u.username, t.tag
FROM articles a
LEFT JOIN `categories` c ON a.category_id = c.category_id
LEFT JOIN `users` u ON a.author_id = u.user_id
LEFT JOIN `tags` t ON a.article_id = t.article_id
ORDER BY a.date_added DESC
I would drop SELECT * and select only the fields that you actually are going to use. This also helps with readability of your code.
How can I add the following code example 1 to example 2 without messing up my query.
Example 1
INNER JOIN users ON users_articles.user_id = users.user_id
Example 2.
SELECT users.*
FROM users_articles
INNER JOIN articles_comments ON users_articles.id = articles_comments.article_id
INNER JOIN users ON articles_comments.user_id = users.user_id
WHERE users.active IS NULL
AND users.deletion = 0
ORDER BY articles_comments.date_created DESC
LIMIT 50
If I understand you correctly, you want to join table users twice, once for comments, and once for articles? In that case, you need to alias the table. I usually use single- or two-letter aliases for brevity even when I do not double tables, but it is not important.
SELECT ...
FROM users_articles UA
INNER JOIN articles_comments AC ON UA.id = AC.article_id
INNER JOIN users UC ON AC.user_id = UC.user_id
AND UC.active IS NULL
AND UC.deletion = 0
INNER JOIN users UA ON UA.user_id = users.user_id
AND UA.active IS NULL
AND UA.deletion = 0
ORDER BY AC.date_created DESC
LIMIT 50
BTW, Don't use SELECT *, it is almost always better to list specifically what you want.
Disclaimer: I might have misunderstood what you are trying to do; posting a bit of context to your code is usually a good idea. In this case, the table names threw me a bit (if it's what I think it is, I'd've just gone with users, articles and comments).