[英]How to select row with the latest timestamp from duplicated rows in a database table?
[英]Slow SQL transaction getting latest timestamp rows from table in Postgres
我在Postgres中進行緩慢的交易時遇到了麻煩,試圖檢索商品的最新價格,這些商品的買入價大於賣出價。 此時,這是一個相當大的表,超過200萬行。 我出於歷史目的使用它。 我目前正在使用的是:
select * from ta_price a
join (
select catalogproduct_id, max(timestamp) ts
from ta_price
group by catalogproduct_id
) b on a.catalogproduct_id = b.catalogproduct_id
and a.timestamp = b.ts
AND buy > sell;
catalogproduct_id是catalogproduct表的外鍵。
在總共2201760行中,它選擇2296行。 總運行時間為181,792.705毫秒。
關於如何改善這一點有什么見解?
編輯:
我被所有答案驚呆了! 我還想在Django ORM的范圍內進一步限定這個問題。 我正在努力在此表上合並組合鍵(或類似鍵)(使用catalogproduct_id和timestamp)。 我有一個主鍵,它是一個自動遞增的索引,我猜它與根本沒有索引一樣好。
編輯2:添加@Erwin建議的部分索引后, CREATE INDEX my_partial_idx ON ta_price (catalogproduct_id, timestamp) WHERE buy > sell;
,我使用來自@wildplasser的查詢大約10-12秒的查詢時間。 為了進一步說明,我的表格是一段時間內產品價格(購買和出售)的快照。 在任何給定時間,我想知道當前(截至其最新快照時間)哪些產品具有“ buy > sell
。
SELECT *
FROM ta_price a
JOIN (
SELECT catalogproduct_id, max(timestamp) ts
FROM ta_price
GROUP BY catalogproduct_id
) b ON a.catalogproduct_id = b.catalogproduct_id
AND a.timestamp = b.ts
AND a.buy > a.sell;
buy
和sell
是不是在你的問題合格。 根據buy > sell
的選擇性,您可以通過在子選擇項中添加相同的WHERE- WHERE
來加快查詢速度。 但是,這會產生不同的結果 。 我將其添加為偶然的機會,以至於您可能忽略了它:
SELECT *
FROM ta_price a
JOIN (
SELECT catalogproduct_id, max(timestamp) ts
FROM ta_price
WHERE buy > sell
GROUP BY catalogproduct_id
) b ON a.catalogproduct_id = b.catalogproduct_id
AND a.timestamp = b.ts
WHERE a.buy > a.sell;
無論哪種方式,像@Will這樣的簡單索引都將有助於:
在ta_price上創建索引my_idx(catalogproduct_id,時間戳記);
不過,有一種更好的方法。
子選擇中的無條件max()
將導致順序表掃描,而不管索引如何。 對於2.2m行,這樣的操作將永遠不會很快。
JOIN
條件與外部SELECT
的WHERE
子句結合使用,將從上述索引中受益。 取決於buy > sell
的選擇性, 部分索引會稍快或明顯快一些,相應地,在磁盤和RAM中會更小:
CREATE INDEX my_partial_idx ON ta_price (catalogproduct_id, timestamp)
WHERE buy > sell;
在這種情況下,索引中列的順序無關緊要。 它還可以加快查詢的第二種速度。
您提到表格是出於“歷史性”目的嗎? 如果這意味着沒有新數據,則可以通過實例化視圖大大加快處理速度。
附帶說明:我不會將timestamp
用作列名。 在PostgreSQL中允許使用,但是在所有SQL標准中都是保留字 。
OK, 最后第一件事 :為2.2米行的表,你需要的方式更多的資源Postgres的具有開箱即用。
shared_buffers
和work_mem
的設置。 增加這些統計信息設置:
ALTER TABLE tmp.ta_price ALTER COLUMN buy SET STATISTICS 1000;
ALTER TABLE tmp.ta_price ALTER COLUMN sell SET STATISTICS 1000;
ALTER TABLE tmp.ta_price ALTER COLUMN ts SET STATISTICS 1000;
然后運行ANALYZE tmp.ta_price;
確保自動真空正在運行。 如有疑問,請運行VACUUM ANALYZE ta_price
,查看其是否有效。
我在資源有限的pg 8.4安裝上測試了wildplasser的測試設置( 這非常有幫助! )。 這是EXPLAIN ANYLYZE
的總運行時間
Erwin 1) 901.487 ms wildplasser 1) 1148.045 ms A.H. 2922.113 ms
帶有附加(購買>出售)子句的變體2:
Erwin 2) 536.678 ms wildplasser 2) 809.215 ms
Erwin 1) 1166.793 ms -- slower (!), than unexpected
計划人員的成本可能已減少,此測試數據庫集群已針對具有更多資源的主數據庫進行了優化。
wildplasser 1) 1122.609 ms -- rest is faster as expected Erwin 2) 481.487 ms wildplasser 2) 769.887 ms
AH的版本需要更長的時間(與您報告的結果相同)。 窗口功能往往很慢,尤其是在較舊版本的postgres上。 我的替代查詢的速度是預期的兩倍。 問題是,是否需要不同的結果-也許不是。
無論如何,那是30萬行。 在使用5年的服務器上,資源有限(但大多數情況下都是適當的設置)的版本8.4的查詢時間為0.5-1s。 擁有不錯的機器和不錯的設置(足夠的RAM!),您至少應將其降至10秒以下。
SELECT * from ta_price a
WHERE NOT EXISTS (
SELECT *
FROM ta_price b
WHERE b.catalogproduct_id = a.catalogproduct_id
AND b.timestamp > a.timestamp
-- AND b.buy > b.sell -- Not clear if OP wants this
)
AND a.buy > a.sell
;
索引(catalogproduct_id,timestamp)可能是有益的。 子查詢中可能需要額外的條件“ AND b.buy> b.sell”(OP中的文本不清楚實際需要什么)。
更新:“時間戳”是保留字。 我改變了一下。 還添加了testdata。
DROP SCHEMA tmp cascade;
CREATE SCHEMA tmp ;
CREATE TABLE tmp.ta_price
( catalogproduct_id INTEGER NOT NULL
, tttimestamp timestamp NOT NULL
, buy DECIMAL (10,2)
, sell DECIMAL (10,2)
);
INSERT INTO tmp.ta_price(catalogproduct_id,tttimestamp,buy,sell)
SELECT serie_n
, serie_t
, serie_v + ((100* random()) - 30)
, serie_v + ((100* random()) - 20)
FROM generate_series (1,10000) serie_n
, generate_series ( '2011-09-01 00:00:00' , '2011-10-01 00:00:00' , '1 day' ::interval) serie_t
, generate_series ( 100 , 100 ) serie_v
;
DELETE FROM tmp.ta_price WHERE random() < 0.02;
CREATE INDEX tmptmp ON tmp.ta_price (catalogproduct_id,tttimestamp);
-- there may be some duplicate records: clear them
DELETE FROM tmp.ta_price a
WHERE EXISTS (SELECT * FROM tmp.ta_price b
WHERE b.catalogproduct_id = a.catalogproduct_id
AND b.tttimestamp = a.tttimestamp
AND b.ctid > a.ctid
);
DROP INDEX tmp.tmptmp ;
ALTER TABLE tmp.ta_price
ADD PRIMARY KEY (catalogproduct_id,tttimestamp)
;
EXPLAIN ANALYZE
SELECT * from tmp.ta_price a
WHERE NOT EXISTS (
SELECT *
FROM tmp.ta_price b
WHERE b.catalogproduct_id = a.catalogproduct_id
AND b.tttimestamp > a.tttimestamp
-- AND b.buy > b.sell -- Not clear if OP wants this
)
AND a.buy > a.sell
;
查詢計划:(用於ta_price中的30萬條記錄)
------------------------------------------------------------------------------
Nested Loop Anti Join (cost=0.00..8607.82 rows=67508 width=38) (actual time=457.486..482.943 rows=4052 loops=1)
-> Seq Scan on ta_price a (cost=0.00..6381.34 rows=101262 width=38) (actual time=0.027..80.256 rows=123142 loops=1)
Filter: (buy > sell)
-> Index Scan using ta_price_pkey on ta_price b (cost=0.00..10.57 rows=506 width=12) (actual time=0.003..0.003 rows=1 loops=123142)
Index Cond: ((b.catalogproduct_id = a.catalogproduct_id) AND (b.tttimestamp > a.tttimestamp))
Total runtime: 483.325 ms
(6 rows)
我會嘗試在catalogproduct_id和timestamp上添加索引。 看起來是沒有其他信息的表格掃描給我。
您的查詢將表與其自身聯系起來以獲得最大值。 您可以嘗試“ 窗口函數 ”來防止這種情況-也許它們在您的情況下效果更好:
SELECT * FROM (
SELECT *, rank() OVER w
FROM ta_price
WINDOW w AS (PARTITION BY catalogproduct_id ORDER BY timestamp DESC)
) c WHERE c.rank = 1 AND c.buy > c.sell;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.