存在(A)和不存在(negA)与自定义聚合

时间:2018-09-17 09:15:21

标签: sql sql-server performance

很多时候,我必须选择进行{criteria set A}交易而不是其他任何类型交易的客户。样本数据:

create table customer (name nvarchar(max))
insert customer values
('George'),
('Jack'),
('Leopold'),
('Averel')

create table trn (id int,customer nvarchar(max),product char(1))
insert trn values
(1,'George','A'),
(2,'George','B'),
(3,'Jack','B'),
(4,'Leopold','A')

比方说,我们要查找所有购买了商品“ A”的顾客,而不是其他顾客(在本例中为B)。 最典型的方法包括将事务表与其自身连接:

select * from customer c
where exists(select 1 from trn p where p.customer=c.name and product='A')
and not exists(select 1 from trn n where n.customer=c.name and product='B')

我想知道是否有更好的方法可以做到这一点。请记住,交易表通常应该很大。

该替代方案如何?

select * from customer c
where exists
(
    select 1
    from trn p
    where p.customer=c.name
    group by p.customer
    having max(case when product='B' then 2 when product='A' then 1 else 0 end)=1
)

交易表仅使用一次的事实会抵消所需的聚合计算吗?

2 个答案:

答案 0 :(得分:1)

您需要测试您的数据的性能。如果您在trn(customer, product)上有一个索引,那么exists的性能通常会非常合理。

使用customers表时尤其如此。

汇总版本的比较效果如何?首先,最好的汇总是:

select customer
from trn
where product in ('a', 'b')
group by customer
having min(product) = 'a' and max(product) = 'b';

如果您在product上有一个索引-并且产品很多(或者很少有具有“ a”和“ b”的客户),那么这可能比not exists版本更快

通常,我建议使用group by,即使它在某些产品上的性能并不总是最佳。为什么?

  • 使用having子句非常灵活,可以处理所有不同的“组内设置”条件。
  • 添加其他条件不会对性能产生很大影响。
  • 如果您不使用客户表,而是使用类似(select distinct customer from trn)的方式,则exists / not exists版本可能会更昂贵。

也就是说,我主张使用group byhaving,因为它更灵活。这意味着在适当的情况下,应该使用其他解决方案。

答案 1 :(得分:0)

您可以尝试以下语句。在某些情况下,它可能比您的声明快,因为它始终会首先确定进行产品A交易的客户,然后在有其他产品的交易时才寻找这些客户。如果真的有好处,则取决于实际表的数据和索引,因此您必须尝试。

WITH customerA AS (SELECT DISTINCT customer FROM trn WHERE product = 'A')
SELECT DISTINCT customer.*
FROM customerA JOIN customer ON customerA.customer = customer.name
WHERE not exists(select 1 from trn n where n.customer = customerA.customer and
product <> 'A')