Skip to content
Advertisement

query for many to many record matching

I have table tag_store like below

enter image description here

I want to filter the ids which has all tag provided in a like

SELECT st.id  from public."tag_store" st
                inner join 
                    (SELECT  x.tg_type,x.tg_value FROM json_to_recordset 
                     ('[{ "tg_type":1, "tg_value ":"cd"},{ "tg_type":2,"tg_value ":"tg"},{ "tg_type":3,"tg_value ":"po" }]  '::json) 
                     AS x (tg_type int, tg_value TEXT)) ftg
                    on  st.tg_type= ftg.tg_type
                        and  st.tg_value = ftg.tg_value order by st.id;

My desired output is it should have output onlye id 1 as it has all three tg_value and tg_id matched..

Please help, what should I change, or is there any better alternate

Thanks

Advertisement

Answer

I would aggregate the values into a JSON array and use the @> operator to filter those that have all of them:

with tags as (
  select id, jsonb_agg(jsonb_build_object('tg_id', tag_id, 'tg_value', tag_value)) all_tags
  from tag_store
  group by id
)  
select *
from tags
where all_tags @> '[{"tg_id":1, "tg_value": "cd"},
                    {"tg_id":2, "tg_value": "tg"},
                    {"tg_id":3, "tg_value": "po"}]'
;

Online example

You can also do that directly in a HAVING clause if you want

select id
from tag_store
group by id
having jsonb_agg(jsonb_build_object('tg_id', tag_id, 'tg_value', tag_value)) 
        @> '[{"tg_id":1, "tg_value": "cd"},
             {"tg_id":2, "tg_value": "tg"},
             {"tg_id":3, "tg_value": "po"}]'
;

Note that this will return IDs that have additional tags apart from those in the comparison array.

User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement