Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/81.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
SQL—查找一对行是否不存在的最有效方法_Sql_Database_Join_Cardinality - Fatal编程技术网

SQL—查找一对行是否不存在的最有效方法

SQL—查找一对行是否不存在的最有效方法,sql,database,join,cardinality,Sql,Database,Join,Cardinality,我在网上似乎找不到类似的情况。我有一个叫做订单的“订单”表,还有一个叫做“订单详细信息”的关于这些订单的详细信息表。某种订单类型的定义是,如果它有两对订单详细信息(值单位对)中的一对(值单位对)。因此,我的订单明细表可能如下所示: order_id | detail ---------|------- 1 | X 1 | Y 1 | Z 2 | X 2 | Z 2 | B 3 | A 3

我在网上似乎找不到类似的情况。我有一个叫做订单的“订单”表,还有一个叫做“订单详细信息”的关于这些订单的详细信息表。某种订单类型的定义是,如果它有两对订单详细信息(值单位对)中的一对(值单位对)。因此,我的订单明细表可能如下所示:

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)。仅检索不包含这两个对中任何一个的订单ID的有效方法是什么?e、 g.对于上表,我只需要收到订单号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表很大,而且我对查询语言非常缺乏经验。有没有一种更快的方法可以用较低的基数来实现这一点?我对表的架构也没有任何控制权,因此我无法在那里更改任何内容。

我将使用聚合和
拥有

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;

首先,我想强调的是,找到最有效的查询是一个好的查询和一个好的索引的组合。我经常在这里看到这样的问题,人们希望魔法只在其中一个地方发生

例如,在各种解决方案中,当没有索引时,您的解决方案是最慢的(在修复语法错误后),但如果在
(详细信息,顺序id)

还请注意,您拥有实际的数据和表结构。您需要尝试各种查询和索引组合,以找到最有效的方法;不仅仅是因为你没有指出你在使用什么平台,而且不同平台的结果可能会有所不同

[/ranf off]


查询 戈登·林诺夫(Gordon Linoff)毫不犹豫地提供了一些好消息。还有另一个可能提供类似性能的选项。你说你不能控制模式;但您可以使用子查询将数据转换为“更友好的结构”

具体而言,如果您:

  • 透视数据,以便根据
    order\u id
  • 以及要检查的每个
    详细信息的列
  • 交叉点是指有多少订单有这个细节
然后您的查询就是:
其中(x=0或y=0)和(a=0或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的查询计划略有不同。这表明在大型数据集上可能存在不同的性能特征


索引 请注意,以上内容可能会处理整个表。因此,从指数中获得的收益很少。但是,如果该表有“长行”,则仅在您正在处理的2列上建立索引意味着需要从磁盘读取的数据更少

您提供的查询结构可能受益于索引,如
(详细信息,订单id)
。这是因为服务器可以更有效地检查子查询条件中的
。效益如何取决于表中的数据分布

作为补充说明,我测试了各种查询选项,包括您和Gordon的固定版本。(不过数据量很小。)

  • 如果没有上述索引,您的查询是批处理中最慢的
  • 使用上述索引,Gordon的第二个查询速度最慢

替代查询 您的查询(已修复):

戈登的第一个和第二个查询之间的混合。修复了第一个中的重复问题和第二个中的性能:

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'
)

子查询中的distinct是无用的,可能不会被DBMS优化掉。您的第二个查询将比第一个查询慢。尝试以下操作:
具有(sum(当val在('X')中时为case,则1 else 0 end)=0或sum(当val在('Y')中时为case,则1 else 0 end)=0)和(sum(当val在('A')中时为case,则1 else 0 end)=0或sum(当val在('B')中时为case,则1 else 0 end)=0)
@CraigYoung。样本数据没有重复项,因此第一个很可能适用于OP。但是您指出了重复的可能性,并提供了第二个查询作为处理该问题的方法。我建议对第二个查询中的having子句进行修改,这样可以在不影响
distinct
性能的情况下实现相同的效果。
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
        )
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'
)