1. 程式人生 > >如何用外部程式優化SQL語句中的IN和EXISTS

如何用外部程式優化SQL語句中的IN和EXISTS

資料結構

IN 和 EXISTS 是 SQL 中常見的複雜條件,在將 SQL(儲存過程)轉換成庫外計算獲取高效能時也會面對這些問題。本文將以 TPC-H 定義的模型為基礎,介紹如何用集算器的語法實現 IN、EXISTS 並做優化。

TPC-H 是 TPC 事務處理效能委員會制定的用於 OLAP 資料庫管理系統的測試標準,模擬真實商業應用環境,以評估商業分析中決策支援系統的效能。TPC-H 模型定義了 8 張表,表結構和表關係如下圖:

IN 常數集合

SQL 示例(1):

select
      P\_SIZE, P\_TYPE, P\_BRAND, count(1) as P\_COUNT
from
      PART
where
      P_SIZE in (2, 3, 8, 15, 17, 25, 27, 28, 30, 38, 41, 44, 45)
      and P_TYPE in ('SMALL BRUSHED NICKEL', 'SMALL POLISHED STEEL')
      and P_BRAND not in ('Brand#12', 'Brand#13')
group by
      P\_SIZE, P\_TYPE, P_BRAND 

select
      P_SIZE, P_TYPE, P_BRAND, count(1) as P_COUNT
from
      PART
where
      P_SIZE in (2, 3, 8, 15, 17, 25, 27, 28, 30, 38, 41, 44, 45)
      and P_TYPE in ('SMALL BRUSHED NICKEL', 'SMALL POLISHED STEEL')
      and P_BRAND not in ('Brand#12', 'Brand#13')
group by
      P_SIZE, P_TYPE, P_BRAND 

優化思路:

如果常數集合元素數少於 3 個則可以翻譯成 (f == v1 || f == v2) 這種樣式,NOT IN 對應的就是(f != v1 && f != v2)。較多的時候可以在外層把常數集合定義成序列,然後用 A.contain(f)來判斷欄位是否在序列中,經驗表明元素個數超過 10 個時二分查詢會明顯快於順序查詢,如果要用二分查詢則需要先把序列排序,然後用 [email protected](f)來進行有序查詢,NOT IN 對應的就是! A.contain(f)。注意一定要把序列定義在迴圈函式外,否則會被多次執行。

如果常數集合元素數量特別多可以用連線過濾,具體請參照下圖程式碼。

集算器實現:

A B
1 =[28, 30, 38,2, 3, 8, 15, 17, 25, 27,50 , 41, 44, 45].sort() / 對常數集合進行排序,這樣就可以用序列的有序查詢,通常序列元素數超過 13 個用有序查詢會比遍歷快
2 =file(PART)[email protected](P_SIZE, P_TYPE, P_BRAND) / 在 PART 表所對應的集檔案上定義遊標,引數為選出列
3 =A2.select([email protected](P_SIZE)&& (P_TYPE == “SMALL BRUSHED NICKEL” P_TYPE == “SMALL POLISHED STEEL”)&& (P_BRAND != “Brand#12” && P_BRAND != “Brand#13”)) / 對遊標附加過濾操作,注意常數序列要定義在過濾函式外面否則會被重複運算
4 =A3.groups(P_SIZE, P_TYPE, P_BRAND; count(1): P_COUNT) / 對遊標計算分組得到最終結果

如果 A1 的元素數量特別多,則可以使用雜湊連線的方法來過濾,把第 3 行程式碼替換如下:

| 3 | =A2.select((P_TYPE == “SMALL BRUSHED NICKEL” || P_TYPE == “SMALL POLISHED STEEL”)&& (P_BRAND != “Brand#12” && P_BRAND != “Brand#13”))[email protected](P_SIZE, A1:~) | // 對遊標附加過濾操作後再附加連線過濾操作 |

IN 子查詢

子查詢選出欄位是主鍵

SQL 示例(2):

select
      PS\_SUPPKEY, count(1) as S\_COUNT
from
      PARTSUPP
where
      PS_PARTKEY in (
            select
                  P_PARTKEY
            from
                  PART
            where
                  P_NAME like 'bisque%%'
      )
group by
      PS_SUPPKEY 

select
      PS_SUPPKEY, count(1) as S_COUNT
from
      PARTSUPP
where
      PS_PARTKEY in (
            select
                  P_PARTKEY
            from
                  PART
            where
                  P_NAME like 'bisque%%'
      )
group by
      PS_SUPPKEY 

優化思路:

子查詢過濾後讀入記憶體,然後外層表與先讀入的記憶體表(子查詢)做雜湊連線進行過濾。集算器提供了 [email protected]()、[email protected]() 兩個函式用來做雜湊連線過濾,switch 是外來鍵式連線,用來把外來鍵欄位變成指引欄位,這樣就可以通過外來鍵欄位直接引用指向表的欄位,join 函式不會改變外來鍵欄位的值,可用於只過濾。

集算器實現:

A B
1 =file(PART)[email protected](P_PARTKEY, P_NAME) / 在 PART 表所對應的集檔案上定義遊標,引數為選出列
2 =A1.select(like(P_NAME, “bisque*”)).fetch() / 對遊標附加過濾操作並取數
3 =file(PARTSUPP)[email protected](PS_SUPPKEY, PS_PARTKEY) / 在 PARTSUPP 表所對應的集檔案上定義遊標,引數為選出列
4 [email protected](PS_PARTKEY, A2:P_PARTKEY) / 對 PARTSUPP 遊標進行連線過濾,@i 選項表示內連線
5 =A4.groups(PS_SUPPKEY; count(1):S_COUNT) / 對遊標計算分組得到最終結果

子查詢選出欄位不是主鍵

SQL 示例(3):

select
      O\_ORDERPRIORITY, count(*) as O\_COUNT      
from
      ORDERS
where
      O_ORDERDATE >= date '1995-10-01'
      and O_ORDERDATE < date '1995-10-01' + interval '3' month
      and O_ORDERKEY in (
            select
                  L_ORDERKEY
            from
                  LINEITEM
            where
                  L\_COMMITDATE< L\_RECEIPTDATE
      )

group by
      O_ORDERPRIORITY 

select
      O_ORDERPRIORITY, count(*) as O_COUNT      
from
      ORDERS
where
      O_ORDERDATE >= date '1995-10-01'
      and O_ORDERDATE < date '1995-10-01' + interval '3' month
      and O_ORDERKEY in (
            select
                  L_ORDERKEY
            from
                  LINEITEM
            where
                  L_COMMITDATE< L_RECEIPTDATE
      )

group by
      O_ORDERPRIORITY 

優化思路:

子查詢過濾後按關聯欄位去重讀入記憶體,然後就變成類似於主鍵的情況了,可以繼續用上面說的 [email protected]()、[email protected]() 兩個函式用來做雜湊連線過濾。

集算器實現:

A B
1 1995-10-01 [email protected](A1,3)
2 =file(LINEITEM)[email protected](L_ORDERKEY,L_COMMITDATE,L_RECEIPTDATE) / 在 LINEITEM 表所對應的集檔案上定義遊標,引數為選出列
3 =A2.select(L_COMMITDATE < L_RECEIPTDATE) / 對遊標附加過濾操作
4 =A3.groups(L_ORDERKEY) / 用 groups 對 L_ORDERKEY 去重
5 =file(ORDERS)[email protected](O_ORDERKEY,O_ORDERDATE,O_ORDERPRIORITY) / 在 ORDER 表所對應的集檔案上定義遊標,引數為選出列
6 =A5.select(O_ORDERDATE>=A1 && O_ORDERDATE < B1) / 對遊標附加過濾操作
7 [email protected](O_ORDERKEY, A4:L_ORDERKEY) / 對 ORDERS 遊標進行連線過濾,@i 選項表示內連線
8 =A7.groups(O_ORDERPRIORITY; count(1):O_COUNT) / 對遊標計算分組得到最終結果

子查詢結果集記憶體放不下

SQL 示例(3):

select
      O\_ORDERPRIORITY, count(*) as O\_COUNT
from
      ORDERS
where
      O_ORDERDATE >= date '1995-10-01'
      and O_ORDERDATE < date '1995-10-01' + interval '3' month
      and O_ORDERKEY in (
            select
                  L_ORDERKEY
            from
                  LINEITEM
            where
                  L\_COMMITDATE< L\_RECEIPTDATE
      )

group by
      O_ORDERPRIORITY 

select
      O_ORDERPRIORITY, count(*) as O_COUNT
from
      ORDERS
where
      O_ORDERDATE >= date '1995-10-01'
      and O_ORDERDATE < date '1995-10-01' + interval '3' month
      and O_ORDERKEY in (
            select
                  L_ORDERKEY
            from
                  LINEITEM
            where
                  L_COMMITDATE< L_RECEIPTDATE
      )

group by
      O_ORDERPRIORITY 

優化思路:

IN 子查詢相當於對子查詢結果集去重然後跟外層表做內連線,而做連線效率較好的就是雜湊連線和有序歸併連線,所以這個問題就變成了怎麼把 IN 翻譯成高效的連線,下面我們來分析在不同的資料分佈下如何把 IN 轉成連線。

(1) 外層表資料量比較小可以裝入記憶體:

先讀入外層表,如果外層表關聯欄位不是邏輯主鍵則去重,再拿上一步算出來的關聯欄位的值對子查詢做雜湊連線過濾,最後拿算出來的子查詢關聯欄位的值對外層表做雜湊連線過濾。

(2) 外層表和內層表按關聯欄位有序:

此時可以利用函式 joinx() 來做有序遊標的歸併連線,如果內層表關聯欄位不是邏輯主鍵則需要先去重。此例中的 ORDERS 表和 LINEITEM 表是按照 ORDERKEY 同序存放,可以利用此方法來做優化。

(3) 內層表是大維表並且按主鍵有序存放:

集算器提供了針對有序大維表文件做連線的函式 A.joinx,其它方法跟記憶體能放下時的處理類似在此不再描述。

集算器實現(1):

A B
1 1995-10-01 [email protected](A1,3)
2 =file(ORDERS)[email protected](O_ORDERKEY,O_ORDERDATE,O_ORDERPRIORITY) / 在 ORDER 表所對應的集檔案上定義遊標,引數為選出列
3 =A2.select(O_ORDERDATE>=A1 && O_ORDERDATE < B1).fetch() / 對遊標附加過濾操作並取數
4 =file(LINEITEM)[email protected](L_ORDERKEY,L_COMMITDATE,L_RECEIPTDATE) / 在 LINEITEM 表所對應的集檔案上定義遊標,引數為選出列
5 =A4.select(L_COMMITDATE < L_RECEIPTDATE)[email protected](L_ORDERKEY,A3:O_ORDERKEY) / 對遊標附加過濾操作和連結過濾操作
6 =A5.groups(L_ORDERKEY) / 對 L_ORDERKEY 去重
7 [email protected](O_ORDERKEY, A6:L_ORDERKEY) / 對排列執行連結過濾操作
8 =A7.groups(O_ORDERPRIORITY;count(1):O_COUNT) / 對排列計算分組得到最終結果

集算器實現(2):

A B
1 1995-10-01 [email protected](A1,3)
2 =file(ORDERS)[email protected](O_ORDERKEY,O_ORDERDATE,O_ORDERPRIORITY) / 在 ORDER 表所對應的集檔案上定義遊標,引數為選出列
3 =A2.select(O_ORDERDATE>=A1 && O_ORDERDATE < B1) / 對遊標附加過濾操作
4 =file(LINEITEM)[email protected](L_ORDERKEY,L_COMMITDATE,L_RECEIPTDATE) / 在 LINEITEM 表所對應的集檔案上定義遊標,引數為選出列
5 =A4.select(L_COMMITDATE < L_RECEIPTDATE) / 對遊標附加過濾操作
6 [email protected](L_ORDERKEY) / 按 L_ORDERKEY 去重
7 =joinx(A3:ORDER, O_ORDERKEY; A6, L_ORDERKEY) / 對有序遊標執行內連線
8 =A7.groups(ORDER.O_ORDERPRIORITY:O_ORDERPRIORITY;count(1):O_COUNT) / 對遊標計算分組得到最終結果

EXISTS 等值條件

此章節的優化思路和 IN 子查詢的優化思路是相同的,事實上這種 EXISTS 也都可以用 IN 寫出來(或者倒過來,把 IN 用 EXISTS 寫出來)。

子查詢關聯欄位是主鍵

SQL 示例(4):

select
      PS\_SUPPKEY, count(1) as S\_COUNT
from
      PARTSUPP
where
      exists (
            select
                  *
            from
                  PART
            where
                  P\_PARTKEY = PS\_PARTKEY
                  and P_NAME like 'bisque%%'
      )

group by
      PS_SUPPKEY 

select
      PS_SUPPKEY, count(1) as S_COUNT
from
      PARTSUPP
where
      exists (
            select
                  *
            from
                  PART
            where
                  P_PARTKEY = PS_PARTKEY
                  and P_NAME like 'bisque%%'
      )

group by
      PS_SUPPKEY 

優化思路:

子查詢過濾後讀入記憶體,然後外層表與先讀入的記憶體表(子查詢)做雜湊連線進行過濾。集算器提供了 [email protected]()、[email protected]() 兩個函式用來做雜湊連線過濾,switch 是外來鍵式連線,用來把外來鍵欄位變成指引欄位,這樣就可以通過外來鍵欄位直接引用指向表的欄位,join 函式不會改變外來鍵欄位的值,可用於只過濾。

集算器實現:

A B
1 =file(PART)[email protected](P_PARTKEY, P_NAME) / 在 PART 表所對應的集檔案上定義遊標,引數為選出列
2 =A1.select(like(P_NAME, “bisque*”)).fetch() / 對遊標附加過濾操作並取數
3 =file(PARTSUPP)[email protected](PS_SUPPKEY, PS_PARTKEY) / 在 PARTSUPP 表所對應的集檔案上定義遊標,引數為選出列
4 [email protected](PS_PARTKEY, A2:P_PARTKEY) / 對 PARTSUPP 遊標進行連線過濾,@i 選項表示內連線
5 =A4.groups(PS_SUPPKEY; count(1):S_COUNT) / 對遊標計算分組得到最終結果

子查詢關聯欄位不是主鍵

SQL 示例(5):

select
      O\_ORDERPRIORITY, count(*) as O\_COUNT
from
      ORDERS
where
      O_ORDERDATE >= date '1995-10-01'
      and O_ORDERDATE < date '1995-10-01' + interval '3' month
      and exists (
            select
                  *
            from
                  LINEITEM
            where
                  L\_ORDERKEY = O\_ORDERKEY
                  and L\_COMMITDATE < L\_RECEIPTDATE
            )

group by
      O_ORDERPRIORITY 

select
      O_ORDERPRIORITY, count(*) as O_COUNT
from
      ORDERS
where
      O_ORDERDATE >= date '1995-10-01'
      and O_ORDERDATE < date '1995-10-01' + interval '3' month
      and exists (
            select
                  *
            from
                  LINEITEM
            where
                  L_ORDERKEY = O_ORDERKEY
                  and L_COMMITDATE < L_RECEIPTDATE
            )

group by
      O_ORDERPRIORITY 

優化思路:

子查詢過濾後按關聯欄位去重讀入記憶體,然後就變成類似於主鍵的情況了,可以繼續用上面說的 [email protected]()、[email protected]() 兩個函式用來做雜湊連線過濾。

集算器實現:

A B
1 1995-10-01 [email protected](A1,3)
2 =file(LINEITEM)[email protected](L_ORDERKEY,L_COMMITDATE,L_RECEIPTDATE) / 在 LINEITEM 表所對應的集檔案上定義遊標,引數為選出列
3 =A2.select(L_COMMITDATE < L_RECEIPTDATE) / 對遊標附加過濾操作
4 =A3.groups(L_ORDERKEY) / 對 L_ORDERKEY 去重
5 =file(ORDERS)[email protected](O_ORDERKEY,O_ORDERDATE,O_ORDERPRIORITY) / 在 ORDER 表所對應的集檔案上定義遊標,引數為選出列
6 =A5.select(O_ORDERDATE>=A1 && O_ORDERDATE < B1) / 對遊標附加過濾操作
7 [email protected](O_ORDERKEY, A4:L_ORDERKEY) / 對 ORDERS 遊標進行連線過濾,@i 選項表示內連線
8 =A7.groups(O_ORDERPRIORITY; count(1):O_COUNT) / 對遊標計算分組得到最終結果

子查詢結果集記憶體放不下

SQL 示例(5):

select
      O\_ORDERPRIORITY, count(*) as O\_COUNT
from
      ORDERS
where
      O_ORDERDATE >= date '1995-10-01'
      and O_ORDERDATE < date '1995-10-01' + interval '3' month
      and exists (
            select
                  *
            from
                  LINEITEM
            where
                  L\_ORDERKEY = O\_ORDERKEY
                  and L\_COMMITDATE < L\_RECEIPTDATE
            )
group by
      O_ORDERPRIORITY 

select
      O_ORDERPRIORITY, count(*) as O_COUNT
from
      ORDERS
where
      O_ORDERDATE >= date '1995-10-01'
      and O_ORDERDATE < date '1995-10-01' + interval '3' month
      and exists (
            select
                  *
            from
                  LINEITEM
            where
                  L_ORDERKEY = O_ORDERKEY
                  and L_COMMITDATE < L_RECEIPTDATE
            )
group by
      O_ORDERPRIORITY 

優化思路:

等值 EXISTS 相當於對內部表關聯欄位去重然後跟外層表做內連線,而做連線效率較好的就是雜湊連線和有序歸併連線,所以這個問題就變成了怎麼把 EXISTS 翻譯成高效的連線,下面我們來分析在不同的資料分佈下如何把 EXISTS 轉成連線。

1、外層表資料量比較小可以裝入記憶體:

先讀入外層表,如果外層表關聯欄位不是邏輯主鍵則去重,再拿上一步算出來的關聯欄位的值對子查詢做雜湊連線過濾,最後拿算出來的子查詢關聯欄位的值對外層表做雜湊連線過濾。

2、外層表和內層表按關聯欄位有序:

此時可以利用函式 joinx() 來做有序遊標的歸併連線,如果內層表關聯欄位不是邏輯主鍵則需要先去重。此例中的 ORDERS 表和 LINEITEM 表是按照 ORDERKEY 同序存放,可以利用此方法來做優化。

3、內層表是大維表並且按主鍵有序存放:

集算器提供了針對有序大維表文件做連線的函式 A.joinx,其它方法跟記憶體能放下時的處理類似在此不再描述。

集算器實現(1):

A B
1 1995-10-01 [email protected](A1,3)
2 =file(ORDERS)[email protected](O_ORDERKEY,O_ORDERDATE,O_ORDERPRIORITY) / 在 ORDER 表所對應的集檔案上定義遊標,引數為選出列
3 =A2.select(O_ORDERDATE>=A1 && O_ORDERDATE < B1).fetch() / 對遊標附加過濾操作並取數
4 =file(LINEITEM)[email protected](L_ORDERKEY,L_COMMITDATE,L_RECEIPTDATE) / 在 LINEITEM 表所對應的集檔案上定義遊標,引數為選出列
5 =A4.select(L_COMMITDATE < L_RECEIPTDATE)[email protected](L_ORDERKEY,A3:O_ORDERKEY) / 對遊標附加過濾操作和連結過濾操作
6 =A5.groups(L_ORDERKEY) / 對 L_ORDERKEY 去重
7 [email protected](O_ORDERKEY, A6:L_ORDERKEY) / 對排列執行連結過濾操作
8 =A7.groups(O_ORDERPRIORITY;count(1):O_COUNT) / 對排列計算分組得到最終結果

集算器實現(2):

A B
1 1995-10-01 [email protected](A1,3)
2 =file(ORDERS)[email protected](O_ORDERKEY,O_ORDERDATE,O_ORDERPRIORITY) / 在 ORDER 表所對應的集檔案上定義遊標,引數為選出列
3 =A2.select(O_ORDERDATE>=A1 && O_ORDERDATE < B1) / 對遊標附加過濾操作
4 =file(LINEITEM)[email protected](L_ORDERKEY,L_COMMITDATE,L_RECEIPTDATE) / 在 LINEITEM 表所對應的集檔案上定義遊標,引數為選出列
5 =A4.select(L_COMMITDATE < L_RECEIPTDATE) / 對遊標附加過濾操作
6 [email protected](L_ORDERKEY) / 按 L_ORDERKEY 去重
7 =joinx(A3:ORDER, O_ORDERKEY; A6, L_ORDERKEY) / 對有序遊標執行內連線
8 =A7.groups(ORDER.O_ORDERPRIORITY:O_ORDERPRIORITY;count(1):O_COUNT) / 對遊標計算分組得到最終結果

EXISTS 非等值條件

同表關聯

SQL 示例(6):

select
      L_SUPPKEY, count(*) as numwait
from
      LINEITEM L1,
where
      L1.L\_RECEIPTDATE > L1.L\_COMMITDATE
      and exists (
            select
                  *
            from
                  LINEITEM L2
            where
                  L2.L\_ORDERKEY = L1.L\_ORDERKEY
                  and L2.L\_SUPPKEY <> L1.L\_SUPPKEY
            )
      and not exists (
            select
                  *
            from
                  LINEITEM L3
            where
                  L3.L\_ORDERKEY = L1.L\_ORDERKEY
                  and L3.L\_SUPPKEY <> L1.L\_SUPPKEY
                  and L3.L\_RECEIPTDATE > L3.L\_COMMITDATE
            )
group by
      L_SUPPKEY 

select
      L_SUPPKEY, count(*) as numwait
from
      LINEITEM L1,
where
      L1.L_RECEIPTDATE > L1.L_COMMITDATE
      and exists (
            select
                  *
            from
                  LINEITEM L2
            where
                  L2.L_ORDERKEY = L1.L_ORDERKEY
                  and L2.L_SUPPKEY <> L1.L_SUPPKEY
            )
      and not exists (
            select
                  *
            from
                  LINEITEM L3
            where
                  L3.L_ORDERKEY = L1.L_ORDERKEY
                  and L3.L_SUPPKEY <> L1.L_SUPPKEY
                  and L3.L_RECEIPTDATE > L3.L_COMMITDATE
            )
group by
      L_SUPPKEY 

優化思路:

我們先來看一下 LINEITEM 表的資料特點,LINEITEM 表的主鍵是 L_ORDERKEY、L_LINENUMBER,一個訂單對應 LINEITEM 裡的多條記錄,這些記錄的 L_ORDERKEY 是相同的並且在資料檔案中是相鄰的。知道這些資訊後再來分析上面的 SQL,其條件是為了找出有多個供應商供貨並且有且僅有一個供應商沒有按時交貨的訂單,因為資料是按訂單順序存放的,這樣我們就可以按訂單有序分組,然後迴圈每組訂單判斷是否有沒按時交貨的訂單項,是否有多個供貨商,並且是不是隻有一個供應商沒有按時交貨。

集算器實現:

A B
1 =file(LINEITEM)[email protected](L_ORDERKEY,L_SUPPKEY,L_RECEIPTDATE,L_COMMITDATE) / 在 LINEITEM 表所對應的集檔案上定義遊標,引數為選出列
2 =A1.group(L_ORDERKEY) / 對有序遊標附加分組, 結果為排列構成的遊標
3 =A2.conj((t=~.select(L_RECEIPTDATE > L_COMMITDATE),if(t.len() > 0 && [email protected](t(1).L_SUPPKEY!=L_SUPPKEY)== null && [email protected](t(1).L_SUPPKEY!=L_SUPPKEY)!= null,t,null))) / 選出每一組中沒有按時發貨的訂單賦值給臨時變數 t,如果 t 長度大於 0 並且 t 中的供應商只有一個並且此組中供應商有多個則返回 t,否則返回 null,conj 相當於 group 的逆操作
4 [email protected](L_SUPPKEY;count(1):numwait) / 對遊標計算分組得到最終結果

總結

在沒有空值的時候帶子查詢的 IN 都可以用 EXISTS 描述,同一個查詢需求用 IN 描述和用 EXISTS 描述翻譯成的集算器程式碼是相同的,所以我們只要弄清楚 EXISTS 怎麼翻譯和優化就知道 IN 怎麼處理了。

等值 exist 本質上是做連線,兩個表做連線效率較好的兩種方式是雜湊連線和有序歸併連線,對於翻譯 select from A where exists (select from B where ) 樣式的 SQL,我們首先要弄清楚下列資訊:

(1)關聯欄位是否是各表的主鍵或者邏輯主鍵

(2)A、B 表的規模,執行其它過濾條件後是否能載入記憶體

(3)如果沒有某個表能裝入記憶體則要考察兩個表是否按關聯欄位有序

如果有一個表能載入記憶體則可以選用雜湊連線的方式來實現,相關的集算器函式有兩個 cs.switch()、cs.join(),這兩個函式有兩個可用的選項 @i、@d 分別對應 exists 和 not exists,引數裡的表要求按關聯欄位值唯一,如果不是邏輯主鍵則要先去重,可用 A.groups()去重。如果兩個表都很大不能載入記憶體則要考察兩個表是否按關聯欄位有序,如果無序可以用 cs.sortx() 排序,對於有序的兩個表就可以用 joinx() 來做連線了。

非等值運算則要分析其中的運算邏輯看能否轉成分組後再計算,如果不能則只能使用巢狀迴圈連線的方式了,對應的函式是 xjoin()。

知道這些資訊並熟練掌握集算器相關的幾個函式後我們就能夠寫出高效的程式碼。

相關推薦

如何用外部程式優化SQL語句INEXISTS

資料結構 IN 和 EXISTS 是 SQL 中常見的複雜條件,在將 SQL(儲存過程)轉換成庫外計算獲取高效能時也會面對這些問題。本文將以 TPC-H 定義的模型為基礎,介紹如何用集算器的語法實現 IN、EXISTS 並做優化。 TPC-H 是 TPC 事務處理效

面試被問之-----sql優化inexists的區別 Mysql in or exists not exists not in區別 (網路整理) Sql語句INexists的區別及應用 [筆記] SQL效能優化 - 避免使用 IN NOT IN

曾經一次去面試,被問及in與exists的區別,記得當時是這麼回答的:''in後面接子查詢或者(xx,xx,xx,,,),exists後面需要一個true或者false的結果",當然這麼說也不算錯,但別人想聽的是sql優化相關,肯定是效率的問題,只是那個時候確實不知道它們在sql優化上的區別,只知道用in會進

Sql語句INexists的區別及應用

應用場景 將不 集中 pre 代碼 根據 gif 效率 .cn   表展示     首先,查詢中涉及到的兩個表,一個user和一個order表,具體表的內容如下:     user表:          order表:        in     確定給定的值是否與子查

實習工作Sql 語句 IN EXISTS 的心得體會

在學校時做專案時資料庫表少而且資料量也不大,做子查詢的時候一般沒有什麼區別,所以一直以來沒有注意過這個問題。 如今工作後,讓我實現政府執法人員執政編號的修改,用IN做子查詢時發現效率特別慢,最後發現是因為IN語句中查詢順序不同導致的。 IN 語句:只執行一次   &n

SQL查詢inexists的區別分析

首先: select * from A where id in (select id from B); select * from A where exists (select 1 from B where A.id=B.id); 對於以上兩種情況,in是在記憶體裡遍

封裝sql語句in限制查詢個數的方法

sel rim () each tar blog and style months /* * 此方法用於每天淩晨取前一天的回滾用戶賬號 */ public function getRollBackAccount($startTime

Sql語句IN等方面的用法

導讀: select * from txt1 select * from txt2 select * from txt1 where name in (select name from txt2 where chk = 0) select * from txt1 where

sql語句in關鍵字提示查詢效能

這幾天一個模組即將結束,便開始進行一些優化,以提升效能。發現一個sql語句的查詢速度是6秒,這明顯讓使用者等待過長。     語句其實挺簡單的,上學的時候經常這麼寫,而且記得好多教科書中也是這麼寫的。 簡要形式: SELECT t1.field1,t1.f

mybatis的xmlsql語句in的寫法

這裡使用 foreach標籤 <foreach  item="item" collection="listTag" index="index"  open="(" separator="," close=")"> #{item} </foreach>

sqlinexists的使用情況

sql中in和exists的使用情況in 和 exists的區別: 如果子查詢得出的結果集記錄較少,主查詢中的表較大且又有索引時應該用in, 反之如果外層的主查詢記錄較少,子查詢中的表大,又有索引時使用exists。其實我們區分in和exists主要是造成了驅動順序的改變(這是性能變化的關鍵),如果是exis

SQL語句IN ORDER BY搭配使用的結果

工作過程中遇到的一個現象,覺得很神奇,記錄一下 不多說,直接上SQL語句 SELECT * FROM `industry_sector` where industry_id IN (11,4,6) ORDER BY industry_id asc 原本我以為出現的結果會

SQLINEXISTS用法的區別

結論 1. in()適合B表比A表資料小的情況 2. exists()適合B表比A表資料大的情況 當A表資料與B表資料一樣大時,in與exists效率差不多,可任選一個使用. select * from A where id in(select id fro

sql語句andor怎麼同時使用

https://zhidao.baidu.com/question/460772793038730925.html邏輯:(A or B)and C 等於 包含了A和C的結果與B和C的結果; 用括號來代表集區分or 和 and; 其他的邏輯可以在資料庫裡試試,這樣可能自己最清楚

mybatissql語句的#$

一直寫mybatis的sql語句,當時想只要實現它的功能就行了,也沒過多的去理解; 首先我們大家都知道#和$都是用來傳值的,但他們之間是怎麼區分的呢?什麼時候用哪個好呢? 1、#將傳入的資料都當成字串

SQLinexists的區別

in和existsin 是把外表和內表作hash 連線,而exists是對外表作loop迴圈,每次loop迴圈再對內表進行查詢。 in (parm1,parm2...), parm是有個數限制的 如果兩個表中一個較小,一個是大表,則子查詢表大的用exists,子查詢表小的用in: 例如:表A(小表),表

SQL查詢inexists、not in、not exists的用法與區別

1、in和exists in是把外表和內表作hash(字典集合)連線,而exists是對外表作迴圈,每次迴圈再對內表進行查詢。一直以來認為exists比in效率高的說法是不準確的,如果查詢的兩個表大小相當,那麼用in和exists差別不大;如果兩個表中一個較小一

sqlinexists的原理及使用場景。

在我們的工作中可能會遇到這樣的情形: 我們需要查詢a表裡面的資料,但是要以b表作為約束。 舉個例子,比如我們需要查詢訂單表中的資料,但是要以使用者表為約束,也就是查詢出來的訂單的user_id要在使用者表裡面存在才返回。 表結構和表資料如下: table1 usertb;+-------+------

數據庫inexists關鍵字的區別

查詢 功能 body member sel 子查詢 ber rom func 數據庫中in和exists關鍵字的區別 in 是把外表和內表作hash join,而exists是對外表作loop,每次loop再對內表進行查詢。 一直以來認為exists比in效

mysql資料庫 IN EXISTS 的誤區

       前言:最近在看 《高效能mysql第三版》 這本書,讀到子查詢優化那章,書中說mysql會將in子查詢改寫成exists查詢(書中基於的mysql版本是5.1.50和5.5),於是乎我又上網找了下資料,發現網上說法幾乎都是: &

mysql inexists的區別

有兩張表:student 和 studentcource student 表 studentcource 表 需求:查詢所有成績小宇60分的同學 in 原理: 1、先查詢 <60 學生得到student_id列表 select student_id