Postgresql_根據執行計劃優化SQL

来源:https://www.cnblogs.com/zhangfx01/archive/2018/08/20/9506420.html
-Advertisement-
Play Games

執行計劃路徑選擇 postgresql查詢規划過程中,查詢請求的不同執行方案是通過建立不同的路徑來表達的,在生成許多符合條件的路徑之後,要從中選擇出代價最小的路徑,把它轉化為一個計劃,傳遞給執行器執行,規劃器的核心工作就是生成多條路徑,然後從中找出最優的那一條。 代價評估 評估路徑優劣的依據是用系統 ...


執行計劃路徑選擇

postgresql查詢規划過程中,查詢請求的不同執行方案是通過建立不同的路徑來表達的,在生成許多符合條件的路徑之後,要從中選擇出代價最小的路徑,把它轉化為一個計劃,傳遞給執行器執行,規劃器的核心工作就是生成多條路徑,然後從中找出最優的那一條。

代價評估

評估路徑優劣的依據是用系統表pg_statistic中的統計信息估算出來的不同路徑的代價(cost),PostgreSQL估計計劃成本的方式:基於統計信息估計計劃中各個節點的成本。PostgreSQL會分析各個表來獲取一個統計信息樣本(這個操作通常是由autovacuum這個守護進程周期性的執行analyze,來收集這些統計信息,然後保存到pg_statistic和pg_class裡面)。

 

用於估算代價的參數postgresql.conf

# - Planner Cost Constants -
​
#seq_page_cost = 1.0            # measured on an arbitrary scale  順序磁碟掃描時單個頁面的開銷
#random_page_cost = 4.0         # same scale as above   隨機磁碟訪問時單頁面的讀取開銷
#cpu_tuple_cost = 0.01          # same scale as above cpu處理每一行的開銷
#cpu_index_tuple_cost = 0.005       # same scale as above cpu處理每個索引行的開銷
#cpu_operator_cost = 0.0025     # same scale as above cpu處理每個運算符或者函數調用的開銷
#parallel_tuple_cost = 0.1      # same scale as above 計算並行處理的成本,如果成本高於非並行,則不會開啟並行處理。
#parallel_setup_cost = 1000.0   # same scale as above
#min_parallel_relation_size = 8MB
#effective_cache_size = 4GB 再一次索引掃描中可用的文件系統內核緩衝區有效大小
​
也可以使用 show all的方式查看

路徑的選擇

--查看表信息
db_jcxxglpt=# \d t_jcxxgl_tjaj
               Table "db_jcxx.t_jcxxgl_tjaj"
    Column    |              Type              | Modifiers 
--------------+--------------------------------+-----------
 c_bh         | character(32)                  | not null
 c_xzdm       | character varying(300)         | 
 c_jgid       | character(32)                  | 
 c_ajbm       | character(22)                  | 
...
Indexes:
    "t_jcxxgl_tjaj_pkey" PRIMARY KEY, btree (c_bh)
    "idx_ttjaj_cah" btree (c_ah)
    "idx_ttjaj_dslrq" btree (d_slrq)
​
首先更新統計信息vacuum analyze t_jcxxgl_tjaj,許多時候可能因為統計信息的不准確導致了不正常的執行計劃
--執行計劃,全表掃描
db_jcxxglpt=# explain (analyze,verbose,costs,buffers,timing)select c_bh,c_xzdm,c_jgid,c_ajbm from t_jcxxgl_tjaj where d_slrq >='2018-03-18';
                                                   QUERY PLAN                                               
------------------------------------------------------------------------------------------------------------
 Seq Scan on db_jcxx.t_jcxxgl_tjaj  (cost=0.00..9.76 rows=3 width=96) (actual time=1.031..1.055 rows=3 loops
=1)
   Output: c_bh, c_xzdm, c_jgid, c_ajbm
   Filter: (t_jcxxgl_tjaj.d_slrq >= '2018-03-18'::date)
   Rows Removed by Filter: 138
   Buffers: shared hit=8
 Planning time: 6.579 ms
 Execution time: 1.163 ms
(7 rows)
--執行計劃,關閉全表掃描
db_jcxxglpt=# set session enable_seqscan = off;
SET
db_jcxxglpt=# explain (analyze,verbose,costs,buffers,timing)select c_bh,c_xzdm,c_jgid,c_ajbm from t_jcxxgl_tjaj where d_slrq >='2018-03-18';
                                                               QUERY PLAN                                                               
------------------------------------------------------------------------------------------------------------
 Index Scan using idx_ttjaj_dslrq on db_jcxx.t_jcxxgl_tjaj  (cost=0.14..13.90 rows=3 width=96) (actual time=0.012..0.026 rows=3 loops=1)
   Output: c_bh, c_xzdm, c_jgid, c_ajbm
   Index Cond: (t_jcxxgl_tjaj.d_slrq >= '2018-03-18'::date)
   Buffers: shared hit=4
 Planning time: 0.309 ms
 Execution time: 0.063 ms
(6 rows)
​
d_slrq上面有btree索引,但是查看執行計劃並沒有走索引,這是為什麼呢?
代價計算:
一個路徑的估算由三部分組成:啟動代價(startup cost),總代價(totalcost),執行結果的排序方式(pathkeys)
代價估算公式:總代價=啟動代價+I/O代價+CPU代價(cost=S+P+W*T)
P:執行時要訪問的頁面數,反應磁碟的I/O次數
T:表示在執行時所要訪問的元組數,反映了cpu開銷
W:表示磁碟I/O代價和CPU開銷建的權重因數
統計信息:統計信息的其中一部分是每個表和索引中項的總數,以及每個表和索引占用的磁碟塊數。這些信息保存在pg_class表的reltuples和relpages列中。我們可以這樣查詢相關信息:
​
--查看統計信息
db_jcxxglpt=# select relpages,reltuples from pg_class where relname ='t_jcxxgl_tjaj';
 relpages | reltuples 
----------+-----------
        8 |       141
(1 row)
​
total_cost = 1(seq_page_cost)*8(磁碟總頁數)+0.01(cpu_tuple_cost)*141(表的總記錄數)+0.0025(cpu_operation_cost)*141(表的總記錄數)=9.7625
​
可以看到走索引的cost=13.90比全表掃描cost=9.76要大。在表較小的情況下,全表掃描比索引掃描更有效, index scan 至少要發生兩次I/O,一次是讀取索引塊,一次是讀取數據塊。

seq_scan源碼

/*
 * cost_seqscan
 *    Determines and returns the cost of scanning a relation sequentially.
 *
 * 'baserel' is the relation to be scanned
 * 'param_info' is the ParamPathInfo if this is a parameterized path, else NULL
 */
void
cost_seqscan(Path *path, PlannerInfo *root,
             RelOptInfo *baserel, ParamPathInfo *param_info)
{
    Cost        startup_cost = 0;
    Cost        cpu_run_cost;
    Cost        disk_run_cost;
    double      spc_seq_page_cost;
    QualCost    qpqual_cost;
    Cost        cpu_per_tuple;
​
    /* Should only be applied to base relations */
    Assert(baserel->relid > 0);
    Assert(baserel->rtekind == RTE_RELATION);
​
    /* Mark the path with the correct row estimate */
    if (param_info)
        path->rows = param_info->ppi_rows;
    else
        path->rows = baserel->rows;
​
    if (!enable_seqscan)
        startup_cost += disable_cost;
​
    /* fetch estimated page cost for tablespace containing table */
    get_tablespace_page_costs(baserel->reltablespace, NULL,&spc_seq_page_cost);
​
    /*
     * disk costs
     */
    disk_run_cost = spc_seq_page_cost * baserel->pages;
​
    /* CPU costs */
    get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost);
​
    startup_cost += qpqual_cost.startup;
    cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;
    cpu_run_cost = cpu_per_tuple * baserel->tuples;
    /* tlist eval costs are paid per output row, not per tuple scanned */
    startup_cost += path->pathtarget->cost.startup;
    cpu_run_cost += path->pathtarget->cost.per_tuple * path->rows;
​
    /* Adjust costing for parallelism, if used. */
    if (path->parallel_workers > 0)
    {
        double      parallel_divisor = get_parallel_divisor(path);
​
        /* The CPU cost is divided among all the workers. */
        cpu_run_cost /= parallel_divisor;
​
        /*
         * It may be possible to amortize some of the I/O cost, but probably
         * not very much, because most operating systems already do aggressive
         * prefetching.  For now, we assume that the disk run cost can't be
         * amortized at all.
         */
​
        /*
         * In the case of a parallel plan, the row count needs to represent
         * the number of tuples processed per worker.
         */
        path->rows = clamp_row_est(path->rows / parallel_divisor);
    }
​
    path->startup_cost = startup_cost;
    path->total_cost = startup_cost + cpu_run_cost + disk_run_cost;
}

一個SQL優化實例

慢SQL:
select c_ajbh, c_ah, c_cbfy, c_cbrxm, d_larq, d_jarq, n_dbjg, c_yqly from db_zxzhld.t_zhld_db dbxx join db_zxzhld.t_zhld_ajdbxx dbaj 
    on dbxx.c_bh = dbaj.c_dbbh where dbxx.n_valid=1 and dbxx.n_state in (1,2,3) and dbxx.c_dbztbh='1003'
    and dbaj.c_zblx='1003' and dbaj.c_dbfy='0' and dbaj.c_gy = '2550' 
    and c_ajbh in (select distinct c_ajbh from db_zxzhld.t_zhld_zbajxx where n_dbzt = 1 and c_zblx = '1003' and c_gy = '2550' ) 
    order by d_larq asc, c_ajbh asc limit 15 offset 0
慢sql耗時:7s
咋們先過下這個sql是乾什麼的、首先dbxx和dbaj的一個join連接然後dbaj.c_ajbh要包含在zbaj表裡面,做了個排序,取了15條記錄、大概就這樣。 
Sql有個缺點就是我不知道查詢的欄位是從那個表裡面取的、建議加上表別名.欄位。
查看該sql的表的數據量:
db_zxzhld.t_zhld_db :1311
db_zxzhld.t_zhld_ajdbxx :341296
db_zxzhld.t_zhld_zbajxx :1027619
        
執行計劃:
01 Limit  (cost=36328.67..36328.68 rows=1 width=107) (actual time=88957.677..88957.729 rows=15 loops=1)
02   ->  Sort  (cost=36328.67..36328.68 rows=1 width=107) (actual time=88957.653..88957.672 rows=15 loops=1)
03         Sort Key: dbaj.d_larq, dbaj.c_ajbh
04         Sort Method: top-N heapsort  Memory: 27kB
05         ->  Nested Loop Semi Join  (cost=17099.76..36328.66 rows=1 width=107) (actual time=277.794..88932.662 rows=8605 loops=1)
06               Join Filter: ((dbaj.c_ajbh)::text = (t_zhld_zbajxx.c_ajbh)::text)
07               Rows Removed by Join Filter: 37018710
08               ->  Nested Loop  (cost=0.00..19200.59 rows=1 width=107) (actual time=199.141..601.845 rows=8605 loops=1)
09                     Join Filter: (dbxx.c_bh = dbaj.c_dbbh)
10                     Rows Removed by Join Filter: 111865
11                     ->  Seq Scan on t_zhld_ajdbxx dbaj  (cost=0.00..19117.70 rows=219 width=140) (actual time=198.871..266.182 rows=8605 loops=1)
12                           Filter: ((n_valid = 1) AND ((c_zblx)::text = '1003'::text) AND ((c_dbfy)::text = '0'::text) AND ((c_gy)::text = '2550'::text))
13                           Rows Removed by Filter: 332691
14                     ->  Materialize  (cost=0.00..66.48 rows=5 width=33) (actual time=0.001..0.017 rows=14 loops=8605)
15                           ->  Seq Scan on t_zhld_db dbxx  (cost=0.00..66.45 rows=5 width=33) (actual time=0.044..0.722 rows=14 loops=1)
16                                 Filter: ((n_valid = 1) AND ((c_dbztbh)::text = '1003'::text) AND (n_state = ANY ('{1,2,3}'::integer[])))
17                                 Rows Removed by Filter: 1297
18               ->  Materialize  (cost=17099.76..17117.46 rows=708 width=32) (actual time=0.006..4.890 rows=4303 loops=8605)
19                     ->  HashAggregate  (cost=17099.76..17106.84 rows=708 width=32) (actual time=44.011..54.924 rows=8605 loops=1)
20                           Group Key: t_zhld_zbajxx.c_ajbh
21                           ->  Bitmap Heap Scan on t_zhld_zbajxx  (cost=163.36..17097.99 rows=708 width=32) (actual time=5.218..30.278 rows=8605 loops=1)
22                                 Recheck Cond: ((n_dbzt = 1) AND ((c_zblx)::text = '1003'::text))
23                                 Filter: ((c_gy)::text = '2550'::text)
24                                 Rows Removed by Filter: 21849
25                                 Heap Blocks: exact=960
26                                 ->  Bitmap Index Scan on i_tzhldzbajxx_zblx_dbzt  (cost=0.00..163.19 rows=5876 width=0) (actual time=5.011..5.011 rows=30458 loops=1)
27                                       Index Cond: ((n_dbzt = 1) AND ((c_zblx)::text = '1003'::text))
28 Planning time: 1.258 ms
29 Execution time: 88958.029 ms
執行計劃解讀:
1:第27->21行,通過索引i_tzhldzbajxx_zblx_dbzt過濾表t_zhld_zbajxx的數據,然後根據過濾條件(c_gy)::text = '2550'::text過濾最終返回8605條數據
2:第17->15行,根據條件過濾t_zhld_db表的數據,最終返回了14條數據
3:第20->19行,對錶t_zhld_zbajxx做group by的操作
4:第13->11行,全表掃描t_zhld_ajdbxx 最終返回了8605條數據
5:第08行,根據t_zhld_ajdbxx返回的8605條結果集作為驅動表和t_zhld_db的結果集(14條)做嵌套迴圈,t_zhld_db的結果集被迴圈了8605次。然後過濾掉了其中的111865條記錄,那麼最終將得到(8605*14-111865) = 8605
6:第07->05行,根據第08和18行返回的結果集最終做了Nested Loop Semi Join,第18行的4303條結果集被迴圈了8605次,(4303*8605-37018710)=8605
7: 第04->02行,對最終的8605條記錄進行排序
8:第01行,limit最終獲取15條記錄
​
整個執行計劃中耗時最長的地方在05行Nested Loop Semi Join,actual time=277.794..88932.662,
表db_zxzhld.t_zhld_db dbxx和db_zxzhld.t_zhld_ajdbxx均是全表掃描

具體優化步驟

查看索引頁並沒有索引,創建c_ajbh,c_dbbh等邏輯外鍵的索引
drop index  if exists I_T_ZHLD_AJDBXX_AJBH;
create index I_T_ZHLD_AJDBXX_AJBH on T_ZHLD_AJDBXX (c_ajbh);
commit;
drop index  if exists I_T_ZHLD_AJDBXX_DBBH;
create index I_T_ZHLD_AJDBXX_DBBH on T_ZHLD_AJDBXX (c_dbbh);
commit;
創建d_larq,c_ajbh的排序索引:
drop index  if exists I_T_ZHLD_AJDBXX_m6;
create index I_T_ZHLD_AJDBXX_m6 on T_ZHLD_AJDBXX (c_zblx,c_dbfy,c_gy,d_larq asc,c_ajbh asc);
commit;
drop index   if exists I_T_ZHLD_ZBAJXX_h3 ;
create index I_T_ZHLD_ZBAJXX_h3 on db_zxzhld.t_zhld_zbajxx  (n_dbzt,c_zblx,c_gy,c_gy);
commit;
​
創建索引後執行計劃有了改變,原來的dbaj表和dbxx表先做nestedloop變成了zbaj和dbaj表先做了nestedloop join,總的cost也從36328.68降到了12802.87,
Limit  (cost=12802.87..12802.87 rows=1 width=107) (actual time=4263.598..4263.648 rows=15 loops=1)
  ->  Sort  (cost=12802.87..12802.87 rows=1 width=107) (actual time=4263.592..4263.609 rows=15 loops=1)
        Sort Key: dbaj.d_larq, dbaj.c_ajbh
        Sort Method: top-N heapsort  Memory: 27kB
        ->  Nested Loop  (cost=2516.05..12802.86 rows=1 width=107) (actual time=74.240..4239.723 rows=8605 loops=1)
              Join Filter: (dbaj.c_dbbh = dbxx.c_bh)
              Rows Removed by Join Filter: 111865
              ->  Nested Loop  (cost=2516.05..12736.34 rows=1 width=140) (actual time=74.083..327.974 rows=8605 loops=1)
                    ->  HashAggregate  (cost=2515.62..2522.76 rows=714 width=32) (actual time=74.025..90.185 rows=8605 loops=1)
                          Group Key: ("ANY_subquery".c_ajbh)::text
                          ->  Subquery Scan on "ANY_subquery"  (cost=2499.56..2513.84 rows=714 width=32) (actual time=28.782..59.823 rows=8605 loops=1)
                                ->  HashAggregate  (cost=2499.56..2506.70 rows=714 width=32) (actual time=28.778..39.968 rows=8605 loops=1)
                                      Group Key: zbaj.c_ajbh
                                      ->  Index Scan using i_t_zhld_zbajxx_h3 on t_zhld_zbajxx zbaj  (cost=0.42..2497.77 rows=715 width=32) (actual time=0.062..15.104 rows=8605 loops=1)
                                            Index Cond: ((n_dbzt = 1) AND ((c_zblx)::text = '1003'::text) AND ((c_gy)::text = '2550'::text))
                    ->  Index Scan using i_t_zhld_ajdbxx_ajbh on t_zhld_ajdbxx dbaj  (cost=0.42..14.29 rows=1 width=140) (actual time=0.015..0.021 rows=1 loops=8605)
                          Index Cond: ((c_ajbh)::text = ("ANY_subquery".c_ajbh)::text)
                          Filter: (((c_zblx)::text = '1003'::text) AND ((c_dbfy)::text = '0'::text) AND ((c_gy)::text = '2550'::text))
                          Rows Removed by Filter: 1
              ->  Seq Scan on t_zhld_db dbxx  (cost=0.00..66.45 rows=5 width=33) (actual time=0.015..0.430 rows=14 loops=8605)
                    Filter: ((n_valid = 1) AND ((c_dbztbh)::text = '1003'::text) AND (n_state = ANY ('{1,2,3}'::integer[])))
                    Rows Removed by Filter: 1298
Planning time: 1.075 ms
Execution time: 4263.803 ms

執行的時間還是要4s左右仍然不滿足需求,並且沒有使用上I_T_ZHLD_AJDBXX_m6這個索引。

等價改寫SQL(1)

等價改寫:將排序條件加入db_zxzhld.t_zhld_ajdbxx讓其先排序,再和t_zhld_db表連接。
修改後sql:
Select dbaj.c_ajbh, dbaj.c_ah, dbaj.c_cbfy, dbaj.c_cbrxm, dbaj.d_larq, dbaj.d_jarq, dbaj.n_dbjg, dbaj.c_yqly 
from (select * from db_zxzhld.t_zhld_db  where  n_valid=1 and n_state in (1,2,3) and c_dbztbh='1003' )dbxx
 join (select * from db_zxzhld.t_zhld_ajdbxx  
where n_valid=1 and c_zblx='1003'
 and c_dbfy='0' and c_gy = '2550' and 
c_ajbh  in (select distinct c_ajbh from db_zxzhld.t_zhld_zbajxx where n_dbzt = 1 and c_zblx = '1003' and c_gy = '2550' ) order by d_larq asc, c_ajbh asc)dbaj
on dbxx.c_bh = dbaj.c_dbbh 
 limit 15 offset 0
再次查看執行計劃:
Limit  (cost=3223.92..3231.97 rows=1 width=107) (actual time=127.291..127.536 rows=15 loops=1)
  ->  Nested Loop  (cost=3223.92..3231.97 rows=1 width=107) (actual time=127.285..127.496 rows=15 loops=1)
        ->  Sort  (cost=3223.64..3223.65 rows=1 width=140) (actual time=127.210..127.225 rows=15 loops=1)
              Sort Key: t_zhld_ajdbxx.d_larq, t_zhld_ajdbxx.c_ajbh
              Sort Method: quicksort  Memory: 2618kB
              ->  Hash Semi Join  (cost=2523.19..3223.63 rows=1 width=140) (actual time=55.913..107.265 rows=8605 loops=1)
                    Hash Cond: ((t_zhld_ajdbxx.c_ajbh)::text = (t_zhld_zbajxx.c_ajbh)::text)
                    ->  Index Scan using i_t_zhld_ajdbxx_m6 on t_zhld_ajdbxx  (cost=0.42..700.28 rows=219 width=140) (actual time=0.065..22.005 rows=8605 loops=1)
                          Index Cond: (((c_zblx)::text = '1003'::text) AND ((c_dbfy)::text = '0'::text) AND ((c_gy)::text = '2550'::text))
                    ->  Hash  (cost=2513.84..2513.84 rows=714 width=32) (actual time=55.802..55.802 rows=8605 loops=1)
                          Buckets: 16384 (originally 1024)  Batches: 1 (originally 1)  Memory Usage: 675kB
                          ->  HashAggregate  (cost=2499.56..2506.70 rows=714 width=32) (actual time=30.530..43.275 rows=8605 loops=1)
                                Group Key: t_zhld_zbajxx.c_ajbh
                                ->  Index Scan using i_t_zhld_zbajxx_h3 on t_zhld_zbajxx  (cost=0.42..2497.77 rows=715 width=32) (actual time=0.043..15.552 rows=8605 loops=1)
                                      Index Cond: ((n_dbzt = 1) AND ((c_zblx)::text = '1003'::text) AND ((c_gy)::text = '2550'::text))
        ->  Index Scan using t_zhld_db_pkey on t_zhld_db  (cost=0.28..8.30 rows=1 width=33) (actual time=0.009..0.011 rows=1 loops=15)
              Index Cond: (c_bh = t_zhld_ajdbxx.c_dbbh)
              Filter: (((c_dbztbh)::text = '1003'::text) AND (n_state = ANY ('{1,2,3}'::integer[])))
Planning time: 1.154 ms
Execution time: 127.734 ms
這一次可以看出,ajdbxx和zbajxx表做了hash semi join 消除了nestedloop,cost降到了3231.97。並且使用上了i_t_zhld_ajdbxx_m6

子查詢中in的結果集有一萬多條數據,嘗試使用exists等價改寫in,看能否有更好的結果

等價改寫SQL(2)

等價改寫:將in替換為exists:
select c_ajbh, c_ah, c_cbfy, c_cbrxm, d_larq, d_jarq, n_dbjg, c_yqly
from (select c_bh from db_zxzhld.t_zhld_db  where n_state in (1,2,3) and c_dbztbh='1003' )dbxx
 join (select c_ajbh, c_ah, c_cbfy, c_cbrxm, d_larq, d_jarq, n_dbjg, c_yqly,c_dbbh from db_zxzhld.t_zhld_ajdbxx   ajdbxx
where c_zblx='1003'
 and c_dbfy='0' and c_gy = '2550' and 
exists (select distinct c_ajbh from db_zxzhld.t_zhld_zbajxx zbajxx where ajdbxx.c_ajbh = zbajxx.c_ajbh and n_dbzt = 1 and c_zblx = '1003' and c_gy = '2550' ) order by d_larq asc, c_ajbh asc)dbaj
on dbxx.c_bh = dbaj.c_dbbh 
 limit 15 offset 0
再次查看執行計劃:
Limit  (cost=1.12..2547.17 rows=1 width=107) (actual time=0.140..0.727 rows=15 loops=1)
  ->  Nested Loop  (cost=1.12..2547.17 rows=1 width=107) (actual time=0.136..0.689 rows=15 loops=1)
        ->  Nested Loop Semi Join  (cost=0.85..2538.84 rows=1 width=140) (actual time=0.115..0.493 rows=15 loops=1)
              ->  Index Scan using i_t_zhld_ajdbxx_m6 on t_zhld_ajdbxx t2  (cost=0.42..700.28 rows=219 width=140) (actual time=0.076..0.127 rows=15 loops=1)
                    Index Cond: (((c_zblx)::text = '1003'::text) AND ((c_dbfy)::text = '0'::text) AND ((c_gy)::text = '2550'::text))
              ->  Index Scan using i_t_zhld_zbajxx_c_ajbh on t_zhld_zbajxx t3  (cost=0.42..8.40 rows=1 width=32) (actual time=0.019..0.019 rows=1 loops=15)
                    Index Cond: ((c_ajbh)::text = (t2.c_ajbh)::text)
                    Filter: (((c_zblx)::text = '1003'::text) AND ((c_gy)::text = '2550'::text) AND (n_dbzt = 1))
        ->  Index Scan using t_zhld_db_pkey on t_zhld_db  (cost=0.28..8.30 rows=1 width=33) (actual time=0.007..0.008 rows=1 loops=15)
              Index Cond: (c_bh = t2.c_dbbh)
              Filter: (((c_dbztbh)::text = '1003'::text) AND (n_state = ANY ('{1,2,3}'::integer[])))
Planning time: 1.268 ms
Execution time: 0.859 ms
​
可以看出使用exist效果更好,最終cost 2547.17
(1).少了t_zhld_zbajxx表的group by操作:Sort Key: t_zhld_ajdbxx.d_larq, t_zhld_ajdbxx.c_ajbh。(這一步是因為使用了索引中的排序)
(2).少了分組的操作:Group Key: t_zhld_zbajxx.c_ajbh。
​
第(2)為什麼這個查詢消除了t_zhld_zbajxx表的group by操作呢?
原因是exists替換了distinct的功能,一旦滿足條件則立刻返回。所以使用exists的時候子查詢可以直接去掉distinct。
​
優化無止境、、、

參考資料:

代價估算

索引和全表掃描

表連接方式

pg11添加了並行hash_join的功能,有興趣可以瞭解:

pg11添加了並行hash join


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 數據列定義 表中數據行的數據插入和數據類型都是基於數據列的,學會添加數據列在開發過程中是必不可少的。 使用SSMS資料庫管理工具添加數據列 在數據表中添加一列或者多列步驟相同 1、連接資料庫,選擇數據表-》右鍵點擊-》選擇設計。 2、在新打開的視窗中輸入中-》輸入列名,數據類型,是否可空-》在下麵輸 ...
  • 1、表欄位的操作 1、語法 :alter table 表名 執行動作; 2、添加欄位(add) alter table 表名 add 欄位名 數據類型; alter table 表名 add 欄位名 數據類型 first; alter table 表名 add 欄位名 數據類型 after 欄位名; ...
  • DECLARE @UserID INT; --推廣員帳號 DECLARE @ProxyID INT; --代理帳號 DECLARE @Score INT=1000; --分數 SELECT @UserID = [SpreaderID] FROM [QPAccountsDB].[dbo].[Accou... ...
  • 使用高版本的出現各種問題: 1. driverClass需要更換為 2. url後面需要加上SSL=false 3. 時間不同步,導致無法鏈接資料庫,提示說是時區設置有問題,可以通過在url後面帶時區設置參數解決 4. 出現在資料庫中找到多張同名的數據表,配置文件中的資料庫名的限制不起作用,而且還生 ...
  • 一.概述 mysql可以在多個平臺上運行,在windows平臺上安裝有noinstall包和圖形化包二種方式。在linux/unix平臺上有RPM包安裝,二進位包(Binary Package)安裝,源碼包(Source package)安裝。 對於RPM包的最大優點是安裝簡單,適合初學者學習使用, ...
  • with cte as ( select belongsAgent from [QPProxyDB].[dbo].[BS_ProxyInfo] where ProxyID = @ProxyID union all select a.ProxyID from [QPProxyDB].[dbo].[BS... ...
  • 一、表操作 1、創建表 1 是否可空,null表示空,非字元串 2 not null - 不可空 3 null - 可空 1 預設值,創建列時可以指定預設值,當插入數據時如果未主動設置,則自動添加預設值 2 create table tb1( 3 nid int not null defalut 2 ...
  • 問題背景 在開發項目過程中,客戶要求使用gbase8s資料庫(基於informix),簡單的分頁頁面響應很慢。排查發現分頁sql是先查詢出數據在外面套一層後再取多少條,如果去掉嵌套的一層,直接獲取則很快。日常使用中postgresql並沒有這樣的操作也很快,這是為什麼呢? 說明 在資料庫實現早期,查 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...