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.
Related
So I created a sql fiddle to explain my problem much clearer:
http://sqlfiddle.com/#!9/3122282/1
As you can see I have 3 tables and 1 of them links the 2 others.
I want to make it so if I say "give me the products that is (color green OR red) and PET (dog)"?
I tried doing:
select `ptl`.`product_id`
from `tags` inner join `tags` as `ptl`
on `tags`.`id` = `ptl`.`tag_id`
where ((`tags`.`tag` = "color" and `tags.value` in ("green", "red"))
or (`tags`.`tag` = "pet" and `tags.value` in ("dog")))
having count(distinct `ptl.tag_id`) = 2
// 2 in that case is the number of tag "category".
but this doesn't seem to work. since having is just checking the count, it will also return the products with 2 color tags without any pet.
You can join the 3 tables, group by product and set the conditions in the HAVING clause:
SELECT p.id, p.name
FROM products p
INNER JOIN product_tags_link pt ON pt.product_id = p.id
INNER JOIN tags t ON pt.tag_id = t.id
GROUP BY p.id, p.name
HAVING SUM(t.tag = 'color' AND t.value IN ('green', 'red')) > 0
AND SUM(t.tag = 'pet' AND t.value IN ('dog')) > 0
See the demo.
You are not joining tags table with product_tags_link and products.
Take this query as a base and add the conditions on the where clause
select *
from products p
inner join product_tags_link ptl on ptl.product_id = p.id
inner join tags t on t.id = ptl.tag_id
where CONDITIONS
a CONDITIONS that can be taken as example
p.id = 1 and t.tag = 'color' and t.value = 'green'
I have a scenario of questions that have tags associated with them - like StackOverflow here. I want to list all the tags associated with a certain question along with a count for each tag that tells how many times this same tag is used/referenced by other questions.
TAGS TABLE: tag_id , tag_name (tag_name is unique)
TAGS CROSS REFERENCE TABLE: tag_id, question_id (tag_id references tag_id in tags table, and question_id in questions table).
QUESTIONS TABLE: question_id, question.
The code I have lists all tags associated with a particular question_id, but the count()/num of the total usage of each tag is always "1", but should be totaling different numbers...
$question_id = 268;
$sql = 'SELECT tags.tag_id, tag_name, count(tags.tag_id) AS num
FROM tags LEFT JOIN tags_x
ON tags.tag_id = tags_x.tag_id
WHERE tags_x.question_id = ?
GROUP BY tags.tag_name';
$stmt = $db->prepare($sql);
$stmt->execute([$question_id]);
$result = $stmt->fetchAll(pdo::FETCH_ASSOC);
$out = '';
foreach($result as $row){
$tag_id = $row['tag_id'];
$tag_name = $row['tag_name'];
$num = $row['num'];//count of all items referencing same tagname
echo $tag_id.' - '.$tag_name.' - '.$num.'<br>';
}
One option uses a correlated subquery to compute the count of questions per tag. I would expect better efficiency, since it avoids the need for outer aggregation:
select
t.tag_id,
t.tag_name,
(select count(*) from tags_x tx1 where tx1.tag_id = t.tag_id) no_questions
from tags t
inner join tags_x tx on tx.tag_id = t.tag_id
where tx.question_id = ?
This gives you an overall count of questions per tag. If you want a count for questions other than the current one, you can substract 1 from the result, or refine the where condition of the subquery:
select
t.tag_id,
t.tag_name,
(select count(*) from tags_x tx1 where tx1.tag_id = t.tag_id and tx1.question_id <> tx.question_id) no_questions
from tags t
inner join tags_x tx on tx.tag_id = t.tag_id
where tx.question_id = ?
When you include tags.tag_id in the SELECT clause, MySql will implicitly include that as part of the GROUP BY. This violates the ansi standard, btw, which would not allow this query at all.
Maybe you wanted count(tags_x.tag_id).
SELECT tags.tag_id, tag_name, count(tags_x.tag_id) AS num
FROM tags
LEFT JOIN tags_x ON tags.tag_id = tags_x.tag_id
WHERE tags_x.question_id = ?
GROUP BY tags.tag_id, tags.tag_name
I'm thinking that we need two references to the tag_x cross reference table, one to get the tags related to our question, and another to get all the references to the same tag.
To get the tag_name returned, we need a join to the tag table.
Something like this:
SELECT t.tag_name
, t.tag_id
, COUNT(c.tag_id) AS cnt_references
FROM tags_x q
JOIN tags_x c
ON c.tag_id = t.tag_id
JOIN tags
ON t.tag_id = q.tag_id
WHERE q.question_id = ?
GROUP
BY t.tag_name
, t.tag_id
ORDER
BY t.tag_name
, t.tag_id
If we don't need to return tag_name, we could avoid the join to the tag table, and just do something like this:
SELECT q.tag_id
, COUNT(c.tag_id) AS cnt_references
FROM tags_x q
JOIN tags_x c
ON c.tag_id = t.tag_id
WHERE q.question_id = ?
GROUP
BY q.tag_id
ORDER
BY q.tag_id
It might be faster to do that, in an inline view, and then join to the tag table later. This should give equivalent result as first query.
SELECT t.tag_name
, t.tag_id
, r.cnt_references
FROM ( -- inline view to count references, one row per tag_id
SELECT q.tag_id
, COUNT(c.tag_id) AS cnt_references
FROM tags_x q
JOIN tags_x c
ON c.tag_id = t.tag_id
WHERE q.question_id = ?
GROUP
BY q.tag_id
) r
JOIN tags t
ON t.tag_id = r.tag_id
ORDER
BY t.tag_name
, t.tag_id
I have the following query:
SELECT DISTINCT (
s.styleTitle
), COUNT(p.id) AS `PictureCount`
FROM `style` s
LEFT JOIN `instagram_picture_style` ps ON s.id = ps.style_id
LEFT JOIN `instagram_shop_picture` p ON ps.picture_id = p.id
LEFT JOIN `instagram_picture_category` c ON c.picture_id = p.id
LEFT JOIN `instagram_second_level_category` sl ON c.second_level_category_id = sl.id
WHERE sl.id =25
GROUP BY p.id
ORDER BY PictureCount
however this query gives me:
I basically wanted the list to be ordered by the style that has the most pictures in it. What did I do wrong? Why is it giving me 1 on all of the styles, I am pretty sure it has more pictures for that style
ORDER BY doesn't have underscores. But equally important, you are using DISTINCT in a way where you seem to think that it is a function. It is not. It is a modifies on the SELECT and it applies to all columns.
You should group by the same column you have in the distinct. Something like this:
SELECT s.styleTitle, COUNT(p.id) AS `PictureCount`
FROM `style` s
LEFT JOIN `instagram_picture_style` ps ON s.id = ps.style_id
LEFT JOIN `instagram_shop_picture` p ON ps.picture_id = p.id
LEFT JOIN `instagram_picture_category` c ON c.picture_id = p.id
LEFT JOIN `instagram_second_level_category` sl ON c.second_level_category_id = sl.id
WHERE sl.id = 25
GROUP BY s.styleTitle
ORDER BY PictureCount DESC;
In fact, you almost never need distinct with group by. If you are using, you need to think why it would be necessary.
I am trying to make a select statement and I just cant get it work.
I have 3 tables:
places, tags, places_tags
Places:
- id
- name
Tags:
- id
- name
Places_tags:
- place_id
- tag_id
- order
I am trying to select places and join the first tag that inserted (using order)
SELECT p.*, t.tag_id AS tag
FROM `places` as p
LEFT JOIN places_tags t ON (t.place_id = p.id)
group by p.id
That's what I have right now.
I need to add somthing like ORDER BY order DESC...
I think that I'm not doing it right.
Something like the following should work:
SELECT
p.name AS "place",
t.name AS "firstTag"
FROM
places p
LEFT JOIN
places_tags pt1
ON pt1.place_id = p.id
LEFT JOIN
places_tags pt2
ON pt2.place_id = p.id AND pt2.tag_id < pt1.tag_id
LEFT JOIN
tags t
ON t.id = pt1.tag_id
WHERE
pt2.tag_id IS NULL
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`