I have three tables:
posts (id, content)
posts_tags (posts_id, tags_id)
tags (id, tag)
How do I select all posts that have (at least) 2 specific tags (lets say tags with id 1 and 2)?
For example, posts table:
id content
---- -------
1 post1
2 post2
3 post3
tags table:
id tag
---- ------
1 tag1
2 tag2
3 tag3
posts_tags table:
posts_id tags_id
---------- ---------
1 1
1 2
2 1
3 1
3 2
3 3
I then expect the following result:
id content
---- ---------
1 post1
3 post3
Post with ID 3 (since it has tags 1, 2, and 3) and post with id 1 (since it has tags with id 1, and 2) but not post 2 since it doesn't have tag with id 2.
Assume I can not change the table structure.
SELECT *
FROM posts p
JOIN posts_tags pt ON pt.posts_id = p.id
WHERE pt.tags_id IN (1,2);
SELECT *
FROM posts p
JOIN posts_tags pt ON pt.posts_id = p.id
WHERE pt.tags_id = 1 OR pt.tags_id = 2;
SELECT *
FROM posts p
JOIN posts_tags pt ON pt.posts_id = p.id
WHERE pt.tags_id = 1 AND pt.tags_id = 2;
EDIT: Quick and dirty
WITH j AS (
SELECT pt.posts_id AS post,
p.content AS content,
STRING_AGG(pt.tags_id::TEXT,',') AS agg
FROM posts p
JOIN posts_tags pt ON pt.posts_id = p.id
GROUP BY pt.posts_id, p.content
)
SELECT post,content
FROM j
WHERE STRING_TO_ARRAY(agg,',') #> ('{2,1}'::TEXT[])
Found an answer to my own question:
SELECT posts.*
FROM posts
INNER JOIN posts_tags ON posts_tags.posts_id = posts.id
INNER JOIN tags ON tags.id = posts_tags.tags_id
WHERE tags.id IN (1, 2)
GROUP BY posts.id
HAVING COUNT(*) > 1
Related
I have table with self-related foreign keys and can not get how I can receive firs child or descendant which meet condition. My_table structure is:
id
parent_id
type
1
null
union
2
1
group
3
2
group
4
3
depart
5
1
depart
6
5
unit
7
1
unit
I should for id 1 (union) receive all direct child or first descendant, excluding all groups between first descendant and union. So in this example as result I should receive:
id
type
4
depart
5
depart
7
unit
id 4 because it's connected to union through group with id 3 and group with id 2 and id 5 because it's connected directly to union.
I've tried to write recursive query with condition for recursive part: when parent_id = 1 or parent_type = 'depart' but it doesn't lead to expected result
with recursive cte AS (
select b.id, p.type_id
from my_table b
join my_table p on p.id = b.parent_id
where b.id = 1
union
select c.id, cte.type_id
from my_table c
join cte on cte.id = c.parent_id
where c.parent_id = 1 or cte.type_id = 'group'
)
Here's my interpretation:
if type='group', then id and parent_id are considered in the same group
id#1 and id#2 are in the same group, they're equals
id#2 and id#3 are in the same group, they're equals
id#1, id#2 and id#3 are in the same group
If the above is correct, you want to get all the first descendent of id#1's group. The way to do that:
Get all the ids in the same group with id#1
Get all the first descendants of the above group (type not in ('union', 'group'))
with recursive cte_group as (
select 1 as id
union all
select m.id
from my_table m
join cte_group g
on m.parent_id = g.id
and m.type = 'group')
select mt.id,
mt.type
from my_table mt
join cte_group cg
on mt.parent_id = cg.id
and mt.type not in ('union','group');
Result:
id|type |
--+------+
4|depart|
5|depart|
7|unit |
Sounds like you want to start with the row of id 1, then get its children, and continue recursively on rows of type group. To do that, use
WITH RECURSIVE tree AS (
SELECT b.id, b.type, TRUE AS skip
FROM my_table b
WHERE id = 1
UNION ALL
SELECT c.id, c.type, (c.type = 'group') AS skip
FROM my_table c
JOIN tree p ON c.parent_id = p.id AND p.skip
)
SELECT id, type
FROM tree
WHERE NOT skip
I have this datatables:
table1
id category
-------------
1 a
2 b
3 c
table2
id heading category_id
----------------------
1 name 1
2 adddress 2
3 phone 3
4 email 1
I want to group this table and display the latest data for that the following query was I used:
SELECT news.id,news.image,news.heading,news.description,
news.date,news.category_id,categories.category
FROM `news`
INNER JOIN categories On news.category_id=categories.id
group by category_id
But I didnt get the latest data that I entered.
Try the query below:
SELECT *
FROM table2 AS tb2 LEFT JOIN table1 AS tb1 ON tb2.category_id = tb1.id
ORDER BY tb1.id
GROUP BY tb2.category_id
I want to combine results on my postgres query, at the product properties. Currently it is giving me these results:
name id value sku item_count
Item # 1 3 Item IT-EM1 3
Item # 1 2 006058465456 IT-EM1 3
Item # 2 3 Item IT-EM2 1
Item # 2 2 055045004505 IT-EM2 1
I would like it to return the following:
name id#1 value#1 id#2 value#2 sku item_count
Item # 1 3 Item 2 006058465456 IT-EM1 3
Item # 2 3 Item 2 055045004505 IT-EM2 1
The id is the product property id (2 being GTIN and 3 being Brand,) the value is the value of that particular product property. My query is below:
SELECT
p.name,
l.property_id AS id,
l.value AS value,
v.sku,
s.count_on_hand AS item_count,
FROM
spree_variants v INNER JOIN
spree_products p ON v.product_id = p.id LEFT OUTER JOIN
spree_stock_items s ON v.id = s.variant_id INNER JOIN
spree_product_properties l ON l.product_id = p.id
WHERE
s.count_on_hand > 0
Any ideas?
Answered this myself. Maybe not the most elegant soltuion, but:
SELECT
p.name,
l.property_id AS id,
l.value AS value,
li.property_id AS id_two,
li.value AS value_two,
v.sku,
s.count_on_hand AS item_count,
FROM
spree_variants v INNER JOIN
spree_products p ON v.product_id = p.id LEFT OUTER JOIN
spree_stock_items s ON v.id = s.variant_id INNER JOIN
spree_product_properties l ON l.product_id = p.id INNER JOIN
spree_product_properties li ON li.product_id = p.id
WHERE
s.count_on_hand > 0 AND
l.property_id = 2 AND
li.property_id = 3
Add the product properties column again as a different variable (l and li) then define in WHERE that l.property_id = 2 and li.property_id = 3
Given a table definition:
Articles:
art_id | name
-------|--------------
1 | article1
2 | article2
3 | article3
Tags:
tag_id | description
-------|--------------
1 | Scientific
2 | Long
3 | Short
article_tags:
art_id | tag_id
-------|---------
1 | 1
1 | 2
2 | 1
2 | 3
3 | 1
3 | 2
3 | 3
The question is How to select all articles that are BOTH Scientific and Short?
Please note, it should be general for [2.N) tag combinations...
You can use the following query to get the result:
select a.art_id, a.name
from articles a
inner join article_tags at
on a.art_id = at.art_id
inner join tags t
on at.tag_id = t.tag_id
where t.description in ('Short', 'Scientific') -- tags here
group by a.art_id, a.name
having count(distinct t.tag_id) = 2 -- total count of tags here
See SQL Fiddle with Demo
Or this could be written:
select a.art_id, a.name
from articles a
inner join article_tags at
on a.art_id = at.art_id
inner join tags t
on at.tag_id = t.tag_id
group by a.art_id, a.name
having
sum(case when t.description = 'Short' then 1 else 0 end) >= 1 and
sum(case when t.description = 'Scientific' then 1 else 0 end) >= ;
See SQL Fiddle with Demo.
If you just want to return the article id, then you could just query the article_tag table:
select art_id
from article_tags
where tag_id in (1, 3)
group by art_id
having count(distinct tag_id) = 2
See SQL Fiddle with Demo
SELECT *
FROM articles
WHERE art_id IN
(
SELECT art_id
FROM article_tags
GROUP BY art_id
HAVING COUNT(art_id) > 1
)
I have records like this in a table called "Entry":
TABLE: Entry
ID Tags
--- ------------------------------------------------------
1 Coffee, Tea, Cake, BBQ
2 Soda, Lemonade
...etc.
TABLE: Tags
ID TagName
---- -----------
1 Coffee
2 Tea
3 Soda
...
TABLE: TagEntry
ID TAGID ENTRYID
--- ----- -------
1 1 1
2 2 1
3 3 2
....
I need to loop through each record in the entire table for Entry, then for each row loop the comma delimited tags because I need to split each tag then do a Tag lookup based on tag name to grab the TagID, and then ultimately insert TagID, EntryID in a bridge table called TagEntry for each comma delimited tag
Not sure how to go about this.
Try this
;with entry as
(
select 1 id, 'Coffee, Tea, Cake, BBQ' tags
Union all
select 2, 'Soda, Lemonade'
), tags as
(
select 1 id,'Coffee' TagName union all
select 2,'Tea' union all
select 3,'Soda'
), entryxml as
(
SELECT id, ltrim(rtrim(r.value('.','VARCHAR(MAX)'))) as Item from (
select id, CONVERT(XML, N'<root><r>' + REPLACE(tags,',','</r><r>') + '</r></root>') as XmlString
from entry ) x
CROSS APPLY x.XmlString.nodes('//root/r') AS RECORDS(r)
)
select e.id EntryId, t.id TagId from entryxml e
inner join tags t on e.Item = t.TagName
This SQL will split your Entry table, for joining to the others:
with raw as (
select * from ( values
(1, 'Coffee, Tea, Cake, BBQ'),
(2, 'Soda, Lemonade')
) Entry(ID,Tags)
)
, data as (
select ID, Tag = convert(varchar(255),' '), Tags, [Length] = len(Tags) from raw
union all
select
ID = ID,
Tag = case when charindex(',',Tags) = 0 then Tags else convert(varchar(255), substring(Tags, 1, charindex(',',Tags)-1) ) end,
Tags = substring(Tags, charindex(',',Tags)+1, 255),
[Length] = [Length] - case when charindex(',',Tags) = 0 then len(Tags) else charindex(',',Tags) end
from data
where [Length] > 0
)
select ID, Tag = ltrim(Tag)
from data
where Tag <> ''
and returns this for the given input:
ID Tag
----------- ------------
2 Soda
2 Lemonade
1 Coffee
1 Tea
1 Cake
1 BBQ