Postgresql

DISTINCT ON vs ROW_NUMBER() 用於獲取具有組最大值的行

  • January 25, 2016

我在 PostgreSQL 9.4 中有下表:

CREATE TABLE dpa(
 id serial NOT NULL,
 currency_id integer,
 amount numeric(14,3),
 date timestamp without time zone,
 plat_id integer,
 pl_id integer,
 p_id integer,
 CONSTRAINT dpa_pkey PRIMARY KEY (id), 
)

和設置:

work_mem = 128MB
table_size = 16 MB

和索引:

CREATE INDEX idx1
 ON dpa
 USING btree
 (plat_id, p_id, pl_id, currency_id, date DESC NULLS LAST, amount)

該表由大約242K行組成。我對列沒有NOT NULL限制,但它們實際上是NOT NULL.

現在,我正在測量查詢的性能:

一世

SELECT plat_id, p_id, pl_id, player_account player_account
FROM(
   SELECT plat_id, p_id, pl_id, 
   COALESCE(amount, 0) player_account,
   ROW_NUMBER() OVER (PARTITION BY plat_id, p_id, pl_id, currency_id
                      ORDER BY date DESC NULLS LAST) rn
   FROM dpa
) sub WHERE rn = 1;

分析方案:

Subquery Scan on sub  (cost=0.42..25484.16 rows=1214 width=44) (actual time=0.044..296.810 rows=215274 loops=1)
 Filter: (sub.rn = 1)
 Rows Removed by Filter: 27556
 ->  WindowAgg  (cost=0.42..22448.79 rows=242830 width=28) (actual time=0.043..255.690 rows=242830 loops=1)
       ->  Index Only Scan using idx1 on dpa  (cost=0.42..16378.04 rows=242830 width=28) (actual time=0.037..91.576 rows=242830 loops=1)"
             Heap Fetches: 242830

伊爾

SELECT DISTINCT ON(plat_id, p_id, pl_id, currency_id)
      plat_id, p_id, pl_id, currency_id, amount
FROM dpa
ORDER BY plat_id, p_id, pl_id, currency_id, date DESC NULLS LAST

分析方案:

Unique  (cost=0.42..18794.73 rows=82273 width=28) (actual time=0.017..128.277 rows=215274 loops=1)
 ->  Index Only Scan using idx1 on dpa  (cost=0.42..16366.43 rows=242830 width=28) (actual time=0.016..72.110 rows=242830 loops=1)
       Heap Fetches: 242830

可以看出,第二個查詢比第一個查詢快。但是當我執行這個查詢時,PGAdmin我得到了以下平均統計數據:

帶有ROW_NUMBER()(第一個)的查詢:4999 ms

帶有DISTINCT ON(第二個)的查詢:5654 ms

我知道bandwith/latency這麼大的結果集的成本很大。所有查詢都會產生215274行。

***問題:***為什麼在第二種情況下接收所有行比在第一種情況下需要更多的時間,儘管計劃器顯示第二種計劃更優化?

您看到的時間由 pgAdmin 給出(但它可以是任何其他客戶端)——這意味著它顯示了獲取呈現輸出所需的時間。正如您知道數據庫生成數據所需的時間(通過使用EXPLAIN ANALYZE),您看到的差異必須來自傳輸和/或渲染。例如,您在第一個查詢中顯示的列較少,這可能是一個原因。

如果您想了解傳輸過程中消耗了多少時間,您可以從應用程序中計算執行查詢的時間。如果您只獲取數據但不以任何方式處理它(更不用說渲染它),您可以很好地估計傳輸數據所需的時間。只需花費獲取數據所需的時間並減去(已知的)數據庫執行時間。

這樣,通過將上述數字與它提供給您的數字進行比較,您也會對 pgAdmin 的渲染時間有所了解。

引用自:https://dba.stackexchange.com/questions/118882