SQL |如果存在另一个等于 (b,c) 的元组,则列出所有所有元组 (a, b, c)

SQL | List all all tuples(a, b, c) if there exists another tuple with equal (b,c)

我有三个表,其中粗体属性是主键

resturant_ID,  name,       ...
1,             Macdonalds   
2,             Hubert
3,             Dorsia
...           ...
restaurant_ID, food_ID, ...
1,             1
1,             4
2,             1
2,             7
...            ...
food_ID    food_name
1          Chips
2          Burgers
3          Salmon
...        ...

我想使用 postgres 列出所有餐厅(restaurant_id 和名称 - 每家餐厅 1 行)与至少一家其他餐厅共享完全相同的一组食物。

比如说

Restaurant_id      name
1                  name1
3                  name3
7                  ...
9                  ...

如有任何帮助,我们将不胜感激!

谢谢

据我了解您的问题,您希望所有餐厅的食物清单都与餐厅 1 相同。

如果是,那就是关系除法问题。这是一种使用连接和聚合的方法:

select r.name
from identifier i1
inner join identifier i2 on i2.food_id = i1.food_id
inner join restaurant r on r.restaurant_id = i2.restaurant_id
where i1.restaurant_id = 1
group by r.restaurant_id
having count(*) = (select count(*) from identifier i3 where i3.restaurant_id = 1)

使用聚合函数string_agg()获取每家餐厅的完整食物列表:

with cte as (
  select restaurant_ID,
         string_agg(food_ID::varchar(10),',' order by food_ID) foods
  from identifier
  group by restaurant_ID
)
select r.* 
from Restaurants r inner join cte c
on c.restaurant_ID = r.restaurant_ID
where exists (select 1 from cte where restaurant_ID <> c.restaurant_ID and foods = c.foods)

但我更愿意根据匹配的食物对餐厅进行分组:

with cte as (
  select restaurant_ID,
         string_agg(food_ID::varchar(10),',' order by food_ID) foods
  from identifier
  group by restaurant_ID
)
select string_agg(r.name, ',') restaurants
from Restaurants r inner join cte c
on c.restaurant_ID = r.restaurant_ID
group by foods
having count(*) > 1

参见demo

这是一种获取具有完全相同食品的独特餐厅集的方法。这使用 array_agg() 和 array_to_string() 函数

 With cte as
(select T.restaurant_id, array_to_string(array_agg(food_id), ',') as food_list
from
(select *
  from Identifier t1
  order by restaurant_id, food_id) T
  group by T.restaurant_id)

select 
   concat(r1.name,',',r2.name) as resturant_names,
   t1.restaurant_id as restaurant_id1,
   r1.name as restaurant_1,
   t2.restaurant_id as restaurant_id2,
   r2.name as restaurant_2,
   t1.food_list as common_food_ids
from cte t1
join cte t2
on t1.restaurant_id < t2.restaurant_id
and t1.food_list = t2.food_list
left join Restaurants r1
on t1.restaurant_id = r1.restaurant_id
left join Restaurants r2
on t2.restaurant_id = r2.restaurant_id;

编辑:这是 dB fiddle - https://dbfiddle.uk/?rdbms=postgres_12&fiddle=e2de05edfbe036cc0d81c64d60f0b599 . Also, just for reference, solution to the same problem in Oracle using listagg function - https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=12785c3d5abbca97be5d44dd45a6da4a

更新:下面的查询解决了问题的更新输出格式。

With cte as
(select T.restaurant_id, array_to_string(array_agg(food_id), ',') as food_list
from
(select *
  from Identifier t1
  order by restaurant_id, food_id) T
  group by T.restaurant_id)

select 
   --concat(r1.name,',',r2.name) as resturant_names,
   t1.restaurant_id as restaurant_id,
   r1.name as restaurant--,
  --t2.restaurant_id as restaurant_id2,
  --r2.name as restaurant_2,
  --t1.food_list as common_food_ids
from cte t1
join cte t2
on t1.restaurant_id = t2.restaurant_id
and t1.food_list = t2.food_list
left join Restaurants r1
on t1.restaurant_id = r1.restaurant_id
left join Restaurants r2
on t2.restaurant_id = r2.restaurant_id;