PostgreSQL SQL優化之NOT IN問題


在我們平時寫SQL時,如果遇到需要排除某些數據時,往往使用id <> xxx and id <> xxx,進而改進為id not in (xxx, xxx);

 

這樣寫沒有問題,而且簡化了SQL,但是往往有些極端情況,使用not in就會造成極大的性能損耗,例如:

 

select * from test where id not in (select id from test_back) and info like '%test%';

 

這樣的話select id from test_back將成為一個子查詢,而且不會走索引,每次走一遍全表掃描。

每一條滿足info like '%test%'的記錄都會去調用這個方法去判斷id是否不在子查詢中,具體的執行計划見下面的例子。

 

改進方法:

1)使用test和test_back進行聯合查詢,id <> id明顯是不行的,這樣只會判斷同一關聯條件下的一行中的id是否相同,無法做到排除某些id。

2)正確的方式應該使用not exists,將條件下推到里面,就不會出現子查詢了:

select * from test  t1 where info like '%test%' and not exits (select 1 from test_back t2 where t2.id = t1.id);

apple=# \d test
                Table "public.test"
 Column |  Type   | Collation | Nullable | Default
--------+---------+-----------+----------+---------
 id     | integer |           | not null |
 info   | text    |           |          |
Indexes:
    "test_pkey" PRIMARY KEY, btree (id)

apple=# truncate test;
TRUNCATE TABLE
apple=# insert into test select generate_series(1, 100), 'test'||round(random()*10000)::text;
INSERT 0 100
apple=# select * from test limit 1;
 id |   info
----+----------
  1 | test9526
(1 row)

apple=# insert into test select generate_series(101, 200), 'tes'||round(random()*10000)::text;
INSERT 0 100                            
apple=# create table test_back as  select * from test where id between 50 and 70;
SELECT 21
apple=# explain select * from test where id not in (select id from test_back) and info like '%test%';
                             QUERY PLAN
---------------------------------------------------------------------
 Seq Scan on test  (cost=25.88..30.88 rows=49 width=12)
   Filter: ((NOT (hashed SubPlan 1)) AND (info ~~ '%test%'::text))
   SubPlan 1
     ->  Seq Scan on test_back  (cost=0.00..22.70 rows=1270 width=4)
(4 rows)

 

apple=# explain select * from test t1 where info like '%test%' and not exists (select 1 from test_back t2 where t2.id = t1.id);
                               QUERY PLAN
-------------------------------------------------------------------------
 Hash Anti Join  (cost=1.47..7.13 rows=89 width=12)
   Hash Cond: (t1.id = t2.id)
   ->  Seq Scan on test t1  (cost=0.00..4.50 rows=99 width=12)
         Filter: (info ~~ '%test%'::text)
   ->  Hash  (cost=1.21..1.21 rows=21 width=4)
         ->  Seq Scan on test_back t2  (cost=0.00..1.21 rows=21 width=4)
(6 rows)

 

 

例子里面沒有建索引,建索引后,這種優化方式效果更好。

 

那么進一步擴展來說:

1)!= 不是標准的SQL,<>才是,這兩個在PostgreSQL中是等效的。

2)exits和not exits的意思是逐條將條件下放到判斷條件,而jion方式是先對表進行笛卡爾積,然后判斷同行之間的各列值是否滿足關系。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM