I have a question.In my database I have 3 tables:
Articles:
id title content date
Tags:
id name
Tags_in_news:
id news_id tag_id
Where news_id is foreign key for news table and tag_id is foreign key for tag table...How to select the articles and all tags attached to them?
I create a query but it select a news for each tag:
SELECT * FROM articles join tags_in_news
ON articles.id = tags_in_news.news_id
join tags on tags.id = tags_in_news.tag_id
ORDER BY date DESC
Try GROUP BY article and grouping tags as comma separated value
something like this:
SELECT
date, a.title, GROUP_CONCAT(DISTINCT t.name) as tags_attached
FROM articles a
JOIN tags_in_news tin ON a.id = tin.news_id
JOIN tags t ON t.id = tin.tag_id
GROUP BY a.id
ORDER BY date DESC
Your query is pretty close, and since you are doing joining it will list all the matching rows and you will get multiple rows for article per tag.
In mysql there is a function called group_concat() which you can use along with group by so that all the tags associated with an article is concat by a comma and then display it for each article.
select
a.title,
a.content,
a.date,
group_concat(t.name) as name
from tags_in_news tin
inner join article a on a.id = tin.news_id
inner join tags t on t.id = tin.tag_id
group by a.id
DEMO
Related
I have one table (posts) that each have a post_id. I also have a table (tags) that holds a tag_id and a name. To tie these together, I have a table (post_tags) which contains a tag_id FK and a post_id FK.
My goal is to get all post columns, with a concated group of tag_ids (easy, done) and a concated group of tag names (hard, haven't figured this out).
This is what I have so far:
SELECT Group_concat(ids SEPARATOR ','),
(SELECT Group_concat(tags.name SEPARATOR ',')
FROM tags
WHERE tags.tag_id = ids) ta
FROM (SELECT post_tags.tag_id AS ids
FROM posts
LEFT JOIN post_tags
ON posts.post_id = post_tags.post_id) tb
This is very close - it correctly group concats the ids, but it only returns a single tag name. Why is this?
Whilst I'm not clear on what grouping you need, the following should help. This query summarises the tags that belong to each post
SELECT
post_tags.post_id
, group_concat(tags.tag_id SEPARATOR ',') tag_ids
, group_concat(tags.name SEPARATOR ',') tag_names
FROM post_tags
INNER JOIN tags ON post_tags.tag_id = tags.tag_id
GROUP BY post_tags.post_id;
Having this database schema (just for illustration purpose)
[articles (id_article, title)]
[articles_tags (id_tag, id_article)]
[tags (id_tag, name)]
using MySQL it's possible to do:
SELECT a.title, COUNT(at.id_tag) tag_count FROM articles a
JOIN articles_tags at ON a.id_article = at.id_article
JOIN tags t ON t.id_tag = at.id_tag
GROUP BY a.id_article
ORDER BY tag_count DESC
resulting in a result where you have on each row article's title and article's tag count, e.g.
mysql for beginner | 8
ajax for dummies | 4
Since ORACLE doesn't support non-aggregated columns in SELECT statement, is it possible to do this anyhow in one query? When you fulfill ORACLE's needs by either adding aggregate function to SELECT statement or adding the column to GROUP BY statement you already get different results.
Thanks in advance
Yes, it's possible. Return id_article in the SELECT list, instead of title, and wrap that whole query in parens to make it an inline view, and then select from that, and a join to the articles table to get the associated title.
For example:
SELECT b.title
, c.tag_count
FROM ( SELECT a.id_article
, COUNT(at.id_tag) tag_count
FROM articles a
JOIN articles_tags at ON a.id_article = at.id_article
JOIN tags t ON t.id_tag = at.id_tag
GROUP BY a.id_article
) c
JOIN articles b
ON b.id_article = c.id_article
ORDER BY c.tag_count DESC
You can also evaluate whether you really need the articles table included in the inline view. We could do a GROUP BY at.id_article instead.
I think this returns an equivalent result:
SELECT b.title
, c.tag_count
FROM ( SELECT at.id_article
, COUNT(at.id_tag) tag_count
FROM articles_tags at
JOIN tags t ON t.id_tag = at.id_tag
GROUP BY at.id_article
) c
JOIN articles b
ON b.id_article = c.id_article
ORDER BY c.tag_count DESC
I've successfully managed to fetch articles filtering by matching tags in an AND manner.
This is my current code:
SELECT *
FROM articles a
JOIN article_tags a_t ON a_t.article_id = a.id
LEFT JOIN tags t ON t.id = a_t.tag_id
WHERE t.caption IN ('fire', 'water', 'earth')
HAVING COUNT(DISTINCT t.caption) = 3
Where:
articles are the articles I want to fetch, with id, title, etc…
tags are the list of tags, with id and caption
article_tags a relationship table, with article_id and tag_id
Now The problem is that after matching, I want to retrieve all the tags that each article has. Even if they are matched by 3 different ones, one may have 5 tags, other 4 tags, and I want them included in each row. Something like "tag,tag,tag" or whatever I can parse, in some "tags" column.
Any ideas? I can't find a way around it...
You need to join your query as a subquery with a query that returns all the tags and combines them with GROUP_CONCAT().
select a.*, GROUP_CONCAT(DISTINCT t.caption) tags
from (select distinct a.*
from articles a
JOIN article_tags a_t on a_t.article_id = a.id
JOIN tags t on t.id = a_t.tag_id
WHERE t.caption IN ('fire', 'water', 'earth')
GROUP BY a.id
HAVING COUNT(DISTINCT t.caption) = 3) a
JOIN article_tags a_t on a_t.article_id = a.id
JOIN tags t on t.id = a_t.tag_id
GROUP BY a.id
BTW, there's no reason to use LEFT JOIN in your query, because you only care about rows with matches in tags.
I also wonder about the need for DISTINCT in the COUNT() -- do you really allow multiple tag IDs with the same caption?
I'm having a problem to select some articles rows depending on a condition.
Here's my problem : All my articles can have several 'tags' attached, so my structure looks like this :
articles articles_tags tags
¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯¯¯¯¯ ¯¯¯¯
id article_id id
title tag_id name
content
[...]
Now I'd like to select ALL articles which have BOTH tags 2 and 3 for example. I tried this :
SELECT * FROM articles a
JOIN articles_tags at
ON (a.id = at.article_id)
WHERE
at.tag_id IN(2, 3)
GROUP BY article_id
But this will select all articles which have AT LEAST tags IDs #2 or #3 (seems logic after all)
Is there any trick or something to get only the articles having a defined list of tag IDs ?
Thanks you
This problem is called Relational Division
SELECT a.*
FROM articles a
INNER JOIN
(
SELECT at.article_id
FROM articles a
INNER JOIN articles_tags at
ON a.id = at.article_id
WHERE at.tag_id IN(2, 3)
GROUP BY at.article_id
HAVING COUNT(*) = 2
) b ON a.id = b.article_id
SQL of Relational Division
I'm building a MySQL tagging DB to allow users to tag products.
My current design uses these tables:
Account
id (PK)
username
Product
id (PK)
product_name
Product_Tag
id (PK)
product_id (FK to Product)
tag_id (FK to Tag)
account_id (FK to Account)
created_date
Tag
id (PK)
tag_name
Note in my design, I'm tracking which user tagged the product and when, and enabling a product to be tagged multiple times using the same tag so I know which tags are most popular for a given product.
I'm trying to figure out a tag search query that returns products most tagged to the searched tags. For example, if I query "red dress", products that are more heavily tagged with "red" and "dress" should appear towards the top in the result set. What query can accomplish this with my existing design?
select max(cnt) from(
select count(*) cnt
from product_tag
where tag_id = <tag id of red dress>
group by product_id);
This problem is most like a Relational Division
SELECT a.ID, a.ProductName
FROM Product a
INNER JOIN Product_Tag b
ON a.ID = b.Product_ID
INNER JOIN Tag c
ON b.Tag_ID = c.ID
WHERE a.ProductName IN ('RED','ADDRESS')
GROUP BY a.ID, a.ProductName
HAVING COUNT(*) >= 2
ORDER BY COUNT(*) DESC
SQL of Relational Division
Try this:
SELECT
P.id,
P.product_name
FROM Product P
LEFT JOIN Product_tag PT
ON P.id = PT.product_id
LEFT JOIN Tag T
On PT.tag_id = T.id
WHERE T.tag_name = 'red'
OR T.tag_name = 'dress'
GROUP BY P.id, P.product_name
ORDER BY COUNT(P.id) DESC
you cant use LIMIT to to get first n most tagged products.