我似乎无法在网上找到类似的情况。我有一个名为Order的'orders'表,以及一个关于这些订单的详细信息表,称为'order detail'。某种类型订单的定义是它是否具有两对订单细节中的一对(价值单位对)。所以,我的订单明细表可能如下所示:
order_id | detail
---------|-------
1 | X
1 | Y
1 | Z
2 | X
2 | Z
2 | B
3 | A
3 | Z
3 | B
两对合在一起的是(X& Y)和(A& B)。检索那些不包含其中任何一对的order_ids的有效方法是什么?例如对于上表,我只需要收到order_id 2。
我能提出的唯一解决方案主要是使用两个查询并执行自联接:
select distinct o.order_id
from orders o
where o.order_id not in (
select distinct order_id
from order_detail od1 where od1.detail=X
join order_detail od2 on od2.order_id = od1.order_id and od2.detail=Y
)
and o.order_id not in (
select distinct order_id
from order_detail od1 where od1.detail=A
join order_detail od2 on od2.order_id = od1.order_id and od2.detail=B
)
问题是性能是一个问题,我的order_detail表是巨大的,我在查询语言方面缺乏经验。有更低的基数有更快的方法吗?我也无法控制表格的模式,因此我无法改变任何内容。
答案 0 :(得分:1)
我会使用聚合和having
:
select order_id
from order_detail od
group by order_id
having sum(case when detail in ('X', 'Y') then 1 else 0 end) < 2 and
sum(case when detail in ('A', 'B') then 1 else 0 end) < 2;
这假定订单没有具有相同detail
的重复行。如果可能的话:
select order_id
from order_detail od
group by order_id
having count(distinct case when detail in ('X', 'Y') then detail end) < 2 and
count(distinct case when detail in ('A', 'B') then detail end) < 2;
答案 1 :(得分:1)
首先,我要强调的是,找到最有效的查询是一个好的查询 和 一个好的索引的组合。我常常在这里看到一些问题,人们只会在其中一个地方寻找魔法。
E.g。在各种解决方案中,当没有索引时,你的解决方案是最慢的(在修复语法错误之后),但对于(detail, order_id)
另请注意,您拥有实际的数据和表格结构。您需要尝试各种查询和索引组合,以找到最有效的方法;不仅仅是因为您还没有表明您正在使用哪个平台,而且平台之间的结果可能会有所不同。
[/ ranf断]
不用多说,戈登·林诺夫提供了一些好的suggestions。还有另一种可能提供类似性能的选择。你说你无法控制架构;但您可以使用子查询将数据转换为更友好的结构。
具体来说,如果你:
order_id
detail
的列然后您的查询就是:where (x=0 or y=0) and (a=0 or b=0)
。以下使用SQL Server的临时表来演示样本数据。无论重复id, val
对,下面的查询都可以正常工作。
/*Set up sample data*/
declare @t table (
id int,
val char(1)
)
insert @t(id, val)
values (1, 'x'), (1, 'y'), (1, 'z'),
(2, 'x'), (2, 'z'), (2, 'b'),
(3, 'a'), (3, 'z'), (3, 'b')
/*Option 1 manual pivoting*/
select t.id
from (
select o.id,
sum(case when o.val = 'a' then 1 else 0 end) as a,
sum(case when o.val = 'b' then 1 else 0 end) as b,
sum(case when o.val = 'x' then 1 else 0 end) as x,
sum(case when o.val = 'y' then 1 else 0 end) as y
from @t o
group by o.id
) t
where (x = 0 or y = 0) and (a = 0 or b = 0)
/*Option 2 using Sql Server PIVOT feature*/
select t.id
from (
select id ,[a],[b],[x],[y]
from (select id, val from @t) src
pivot (count(val) for val in ([a],[b],[x],[y])) pvt
) t
where (x = 0 or y = 0) and (a = 0 or b = 0)
值得注意的是,上面选项1和2的查询计划略有不同。这表明大数据集可能具有不同的性能特征。
请注意,以上内容可能会处理整个表格。因此索引几乎无法获得。但是,如果表格有&#34;长行&#34;,那么您只使用2个列的索引意味着需要从磁盘读取更少的数据。
您提供的查询结构可能会受益于(detail, order_id)
等索引。这是因为服务器可以更有效地检查NOT IN
子查询条件。有益程度取决于表中数据的分布。
作为附注,我测试了各种查询选项,包括你的固定版本和Gordon's。 (虽然只有很小的数据大小。)
您的查询(已修复):
select distinct o.id
from @t o
where o.id not in (
select od1.id
from @t od1
inner join @t od2 on
od2.id = od1.id
and od2.val='Y'
where od1.val= 'X'
)
and o.id not in (
select od1.id
from @t od1
inner join @t od2 on
od2.id = od1.id
and od2.val='a'
where od1.val= 'b'
)
戈登的第一次和第二次查询之间的混合。修复了第一个中的重复问题和第二个中的性能:
select id
from @t od
group by id
having ( sum(case when val in ('X') then 1 else 0 end) = 0
or sum(case when val in ('Y') then 1 else 0 end) = 0
)
and( sum(case when val in ('A') then 1 else 0 end) = 0
or sum(case when val in ('B') then 1 else 0 end) = 0
)
使用INTERSECT和EXCEPT:
select id
from @t
except
(
select id
from @t
where val = 'a'
intersect
select id
from @t
where val = 'b'
)
except
(
select id
from @t
where val = 'x'
intersect
select id
from @t
where val = 'y'
)