2017-04-07 28 views
1

我似乎無法找到與我的在線類似的情況。我有一個名爲Order的'orders'表和一個表,用於詳細說明這些訂單,稱爲'order detail'。某種訂單類型的定義是,它是否具有兩對訂單明細(價值單位對)中的一個。所以,我的訂單詳細信息表可能是這樣的:SQL - 查找一對行是否不存在的最有效方法

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_id的有效方式?例如對於上面的表格,我需要只接收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

子查詢內部的不同是無用的,可能不是你的DBMS –

回答

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的查詢計劃略有不同。這表明與大型數據集不同的性能特徵的可能性。


指標

注意上面可能會處理整個表。所以從索引中獲得的東西很少。但是,如果表格有「長行」,那麼僅處理2列的索引意味着需要從磁盤讀取更少的數據。

您提供的查詢結構可能受益於諸如(detail, order_id)之類的索引。這是因爲服務器可以更有效地檢查NOT IN子查詢條件。如何受益取決於表中數據的分佈。

作爲一個便箋,我測試了各種查詢選項,包括您的和戈登的固定版本。 (儘管只有很小的數據大小)

  • 沒有上述索引,您的查詢在批處理中是最慢的。
  • 有了上述指標,Gordon的第二個查詢是最慢的。

的替代查詢

您的查詢(固定):戈登的第一和第二查詢之間

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' 
) 
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; 
+0

被優化掉你的第二個查詢會比第一個查詢慢。嘗試以下方法:當('(X')中的val'then 1 else 0 end')= 0 或總和(當('Y')中的val then 1 else 0 end)= 0 ) 和(總和(當('A')中的val'then 1 else 0 end')= 0 或總和(當('B')中的val then 1 else 0 end)= 0 )' –

+0

@CraigYoung。 。 。樣本數據沒有重複,所以很有可能是OP的第一部作品。 –

+0

同意。但是你提出了重複的可能性,並提出了第二個查詢作爲解決這個問題的方法。我建議對第二個查詢的having子句進行更改,以達到相同的效果,而不會影響'distinct'的性能。 –

相關問題