Select 位客户只订购了特定的会议,none 位其他客户

Select clients that ordered only specific conferences and none else

假设我们有一个客户 table 和一个订单 table,它们像这样链接在一起:client.id = order.client_id。订单 table 条目包含已购买的产品 ID。产品 table 条目包含购买此产品的会议 ID。
我如何 select 只从特定会议列表中购买产品的客户?

我可以用 PHP 脚本解决这个问题,发出多个请求,重新排列数据,但这并不好玩。

到目前为止我试过这个:

select
    o.client_id,
    array_agg(p.conference_id) as conference_ids
from product as p
left join order as o
    on o.id = p.order_id
where
    p.conference_id = any('{ 46545, 46543 }'::int[])

但这并没有奏效,因为 select 的客户不仅从这些会议而且还从其他会议购买了产品。

编辑:修复 sql 语法正确

一种不使用数组的方法是:

select client.id
from product as p
left join order as o on o.id = p.order_id
group by client.id
having count(*) filter(where p.conference_id not in (46545, 46543)) = 0;

如果您还想断言上述两个会议中的 both 也参加了(即参加了两个会议,但没有参加其他会议),您可以向 [= 添加另一个断言12=] 子句:

select client.id
from product as p
left join order as o on o.id = p.order_id
group by client.id
having count(*) filter(where p.conference_id not in (46545, 46543)) = 0 and
       min(p.conference_id) <> max(p.conference_id);

您可以使用聚合,并使用 having 子句进行过滤。

您的查询引用了 table client,而您没有引用 select - 这是一个语法错误。您可以直接从订单 table 中获取 client_id

select o.client_id, array_agg(p.conference_id) as conference_ids
from orders as o 
inner join product as p on p.product_id = o.id
group by o.client_id
having count(*) filter(where not p.conference_id = any('{ 46545, 46543 }'::int[])) = 0

请注意 order 是一个语言关键字,因此 table 名称不是一个好的选择;我将其重命名为 orders

如果要确保客户端对数组中列出的所有会议都下单,可以在having子句中添加另一个条件;您可以使用 array_length():

使其独立于数组元素的数量
having 
    count(distinct p.conference_id) 
        filter(where p.conference_id = any('{ 46545, 46543 }'::int[])) = array_length('{ 46545, 46543 }'::int[], 1)
    and count(*) filter(where not p.conference_id = any('{ 46545, 46543 }'::int[])) = 0

推测您有一个客户table。一种有趣的方法是使用集合操作:

select c.id
from clients c
except
select o.client_id
from orders o join
     products p
     on o.id = p.order_id
where p.conference_id not in (46545, 46543);

实际上,我更喜欢聚合方法——它们更通用。但是您问题中的查询在语法上不正确,我觉得您想使用 clients table.