Nested query on join? - mysql

SELECT adminTags.Tag, URL, Votes
FROM `adminTags`
LEFT JOIN `Tags` ON adminTags.Tag = Tags.Tag
I'm joining table adminTags with table tags.
How can I first
SELECT *
FROM `Tags`
WHERE URL = "$site"
and then join this to adminTags?

You mean...
SELECT adminTags.Tag, URL, Votes FROM `adminTags`
LEFT JOIN `Tags` ON adminTags.Tag = Tags.Tag AND Tags.URL = "$site"
I suspect you tried first to add WHERE URL = "$site" which will effectively make this an INNER JOIN (since the WHERE would not be satisfied without the URL also being satisfied.
Putting the condition in the ON resolves that — you're joining only when there's a match.

SELECT adminTags.Tag, URL, Votes
FROM `adminTags` LEFT JOIN `Tags`
ON adminTags.Tag = Tags.Tag
WHERE `Tags`.URL = "$site"

SELECT adminTags.Tag, adminTags.URL, adminTags.Votes FROM `Tags`
LEFT JOIN `adminTags` ON adminTags.Tag = Tags.Tag
WHERE adminTags.URL = "$site"

There are two options when using an OUTER join, and the results can be very different so details matter.
SELECT at.Tag,
t.url, t.votes
FROM adminTags at
LEFT JOIN tags t ON t.tag = at.tag
AND t.url = $site
This query will apply the t.url = $site before the join is made, giving you pre-filtered results.
SELECT at.Tag,
t.url, t.votes
FROM adminTags at
LEFT JOIN tags t ON t.tag = at.tag
WHERE t.url = $site
The query above applies the criteria after the join has been made. It does not "convert an OUTER JOIN into an INNER JOIN" -- it filters the result set after the OUTER JOIN has been made.
In this example, there's no difference between the result sets. However, if the URL column say was not nullable (value could not be NULL) you could check for NULLs (which would tell you what ADMINTAGS don't have corresponding TAGS records:
SELECT at.Tag,
t.url, t.votes
FROM adminTags at
LEFT JOIN tags t ON t.tag = at.tag
WHERE t.url IS NULL
...whereas the following would return NULL for all references to TAGS (while not accurately demonstrating ADMINTAGS rows without related TAGS rows:
SELECT at.Tag,
t.url, t.votes
FROM adminTags at
LEFT JOIN tags t ON t.tag = at.tag
AND t.url IS NULL

Related

subquery with an inner join

I'm stuck on a subquery issue, trying to get multiple columns while using a join.
I'm trying to grab different fields from other tables, based on what the "type" field is set as, for a notifications system that tracks different sections of a site.
I was hoping I could do something like this:
SELECT
n.`id`,
n.`last_date`,
IF(n.type = 'liked', (SELECT co.`article_id`, a.article_id FROM `articles_comments` co INNER JOIN `articles` a ON a.article_id = co.article_id WHERE co.`comment_id` = n.data_id), NULL),
n.`comment_id`,
n.`type`,
n.`data_id`,
FROM
`user_notifications` n
WHERE n.`owner_id` = 8505
The problem, is that phpmyadmin gives me an error of "#1241 - Operand should contain 1 column(s)"
I'm pretty confused, because if I do no INNER JOIN and only grab the "article_id" it works fine.
Sub-query must return only one column
you can see this link
this query
(SELECT a.article_id
FROM `article
s_comments` co
INNER JOIN `articles` a
ON a.article_id = co.article_id
WHERE co.`comment_id` = n.data_id)
must be something like this
(SELECT a.article_id
FROM `article
s_comments` co
INNER JOIN `articles` a
ON a.article_id = co.article_id
WHERE co.`comment_id` = n.data_id)
UNION ALL
(SELECT co.`article_id`
FROM `article
s_comments` co
INNER JOIN `articles` a
ON a.article_id = co.article_id
WHERE co.`comment_id` = n.data_id)

Trying to JOIN 5 tables, having issues

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.

multiple AND on same joined table coulmnn

I have three table images, image_tags and tags. image table contains images info, tags contains tags info and image_tags contains relationship between images and tags, relationship is many to many. I want to filter images based on multiple tags with AND condition (not IN).
I have tried :
SELECT images.* FROM images
LEFT JOIN image_tags ON image_tags.image_id = images.id
LEFT JOIN tags tag_0 ON image_tags.tag_id = tag_0.id
LEFT JOIN tags tag_1 ON image_tags.tag_id = tag_1.id
WHERE tag_0.tag = "tagme"
AND tag_1.tag = "excellent"
AND images.filesize > 0
GROUP BY images.id
ORDER BY images.posted DESC LIMIT 0, 40
AND
SELECT images.* FROM images
LEFT JOIN image_tags ON image_tags.image_id = images.id
LEFT JOIN tags ON image_tags.tag_id = tags.id
WHERE tags.tag = "tagme"
AND tags.tag = "excellent"
AND images.filesize > 0
GROUP BY images.id
ORDER BY images.posted DESC LIMIT 0, 40
But nothing worked it is always returning zero results though they exist.
Your first query, even though it uses a LEFT join, requires hits in tags on both "tagme" and "excellent", because the condition is in the where clause. Move the condition into the ON clause, like this:
SELECT images.* FROM images
LEFT JOIN image_tags ON image_tags.image_id = images.id
LEFT JOIN tags tag_0 ON image_tags.tag_id = tag_0.id AND tag_0.tag = 'tagme'
LEFT JOIN tags tag_1 ON image_tags.tag_id = tag_1.id AND tag_1.tag = 'excellent'
WHERE images.filesize > 0
GROUP BY images.id
ORDER BY images.posted DESC LIMIT 0, 40
Your second query is way off - it has an impossible condition:
WHERE tags.tag = "tagme"
AND tags.tag = "excellent"
tags.tag can not be both "tagme" and "excellent".
None of your attempt is correct enough, though the first one is closer to the correct one. To filter the tags the way you are trying to filter them, you'll need to join to image_tags twice and join both instances of tags to separate instances of image_tags. And it seems like you want to do inner joins rather than outer joins. Here:
SELECT i.*
FROM images i
INNER JOIN image_tags it0 ON it0.image_id = i.id
INNER JOIN tags t0 ON t0.id = it0.tag_id
INNER JOIN image_tags it1 ON it1.image_id = i.id
INNER JOIN tags t1 ON t1.id = it1.tag_id
WHERE i.filesize > 0
AND t0.tag = 'tagme'
AND t1.tag = 'excellent'
ORDER BY i.posted DESC
LIMIT 0, 40
There's another way you could do the same. Try the following:
SELECT i.*
FROM images i
INNER JOIN image_tags it ON it.image_id = i.id
INNER JOIN tags t ON t.id = it.tag_id
WHERE i.filesize > 0
AND t.tag IN ('tagme', 'excellent')
GROUP BY i.id
HAVING COUNT(DISTINCT i.tag) = 2
ORDER BY i.posted DESC
LIMIT 0, 40
The t.tag IN ('tagme', 'excellent') condition limits allowed tags to the specified list and HAVING COUNT(DISTINCT i.tag) = 2 makes sure the image has got all of them.

Mysql joins problem

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.

Complex search problem in mysql query

I have models(tables) in my database with table and fields name like
tags (id, name)
taggings (id, tag_id, taggable_id, taggable_type, context)
employment_histories (id, user_id, grades, subjects, my_interests )
users (id)
taggable_id is actually employment_histories_id and context can either be grade or subjects or my_interests
now I have array of tags e.g. g={"9th","10th"}
and I want to get users, only whose tags are all matching to the above g array.
I've written the query below:
SELECT DISTINCT users.* FROM `users`
LEFT OUTER JOIN `employment_histories`
ON `employment_histories`.`user_id` = `users`.`id`
LEFT OUTER JOIN `taggings`
ON `employment_histories`.`id` = `taggings`.`taggable_id`
AND `taggings`.`taggable_type` = 'EmploymentHistory'
LEFT OUTER JOIN `tags` ON taggings.context = 'subjects'
WHERE tags.name='9th' OR tags.name='10th'
but it gives me those users too, which match any of the tags, however I want that it will return only that user who match all the two tags
Suppose that tags 9th and 10th have tag id 9 and 10 then what i want that it will only return the taggable_id(which is employmenthistories.id) who has common taggable_id for these two tag_id (that is 9 and 10) in taggings table
for example i have two user tariq and kamal and both of these users have 9th tag common but kamal dont have tag 10th so want query which if passed these two tags should return only tariq whose tags are all macthing these two tags but users like kamal which match any of the tags should be filtered too
From php chat room:
SELECT
users.* ,
count(*) AS count
FROM users
LEFT JOIN employment_histories ON users.id = employment_histories.user_id
LEFT JOIN tagging ON tagging.taggable_id = employment_histories.id
LEFT JOIN tags ON tags.id = tagging.tag_id
WHERE tags.name = "9th"
OR tags.name = "10th"
GROUP BY users.id
HAVING count = 2
SELECT users.* FROM users
INNER JOIN employment_histories
ON employment_histories.user_id = users.id
INNER JOIN taggings
ON employment_histories.id = taggings.taggable_id
AND taggings.taggable_type = 'EmploymentHistory'
AND taggings.context = 'subjects'
INNER JOIN tags ON tags.id = taggings.tag_id
WHERE tags.name IN ('9th','10th')
GROUP BY users.id
HAVING COUNT(DISTINCT(tags.name)) = 2;
I have re-wrote the query.
Few changes:
Joining Tags on tags.id = taggings.tag_id
Remove OR from where clause, and use in, improves the performance.
SELECT DISTINCT users.*, count(*) as totRow FROM `users`
LEFT OUTER JOIN `employment_histories`
ON `employment_histories`.`user_id` =
`users`.`id` LEFT OUTER JOIN
`taggings` ON
`employment_histories`.`id` =
`taggings`.`taggable_id` AND
`taggings`.`taggable_type` =
'EmploymentHistory' AND
`taggings`.`context` = 'subjects'
LEFT OUTER JOIN `tags` ON `tags`.`id` = `taggings`.`tag_id`
WHERE tags.name = '9th' or tags.name = '10th'
GROUP BY `users`.`id`