SQL |如果存在另一个等于 (b,c) 的元组,则列出所有所有元组 (a, b, c)
SQL | List all all tuples(a, b, c) if there exists another tuple with equal (b,c)
我有三个表,其中粗体属性是主键
- 餐厅(restaurant_ID, 名称, ...)
resturant_ID, name, ...
1, Macdonalds
2, Hubert
3, Dorsia
... ...
- 标识符(restaurant_ID,food_ID)
restaurant_ID, food_ID, ...
1, 1
1, 4
2, 1
2, 7
... ...
- 食物(food_ID, 姓名, ...)
food_ID food_name
1 Chips
2 Burgers
3 Salmon
... ...
我想使用 postgres 列出所有餐厅(restaurant_id 和名称 - 每家餐厅 1 行)与至少一家其他餐厅共享完全相同的一组食物。
比如说
- ID 为“1”的餐厅 仅 关联了 food_id 的 1 和 4 ,如
Identifier
[=64 所示=]
- ID 为“3”的餐厅 仅 关联了 food_id 的 4 和 1 ,如
Identifier
[=64 所示=]
- ID 为“7”的餐厅 仅 关联了 food_id 的 6 ,如
Identifier
所示
- ID 为“9”的餐厅有 仅 关联 food_id 的 6 ,如
Identifier
所示
- 然后输出
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;
我有三个表,其中粗体属性是主键
- 餐厅(restaurant_ID, 名称, ...)
resturant_ID, name, ...
1, Macdonalds
2, Hubert
3, Dorsia
... ...
- 标识符(restaurant_ID,food_ID)
restaurant_ID, food_ID, ...
1, 1
1, 4
2, 1
2, 7
... ...
- 食物(food_ID, 姓名, ...)
food_ID food_name
1 Chips
2 Burgers
3 Salmon
... ...
我想使用 postgres 列出所有餐厅(restaurant_id 和名称 - 每家餐厅 1 行)与至少一家其他餐厅共享完全相同的一组食物。
比如说
- ID 为“1”的餐厅 仅 关联了 food_id 的 1 和 4 ,如
Identifier
[=64 所示=] - ID 为“3”的餐厅 仅 关联了 food_id 的 4 和 1 ,如
Identifier
[=64 所示=] - ID 为“7”的餐厅 仅 关联了 food_id 的 6 ,如
Identifier
所示 - ID 为“9”的餐厅有 仅 关联 food_id 的 6 ,如
Identifier
所示 - 然后输出
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;