我目前工作的一個複雜的排序問題的Postgres 9.2 你可以找到源代碼在這個問題(簡化版)使用此:http://sqlfiddle.com/#!12/9857e/11PostgreSQL的排序連接表與索引
我有一個巨大的( >> 20Mio行)包含各種不同類型列的表。
CREATE TABLE data_table
(
id bigserial PRIMARY KEY,
column_a character(1),
column_b integer
-- ~100 more columns
);
可以說,我想排序此表超過2列(ASC)。 但我不想用簡單的Order By做到這一點,因爲稍後我可能需要在排序的輸出中插入行,用戶可能只希望看到100行(排序後的輸出) 。
爲了實現這些目標,我這樣做:
CREATE TABLE meta_table
(
id bigserial PRIMARY KEY,
id_data bigint NOT NULL -- refers to the data_table
);
--Function to get the Column A of the current row
CREATE OR REPLACE FUNCTION get_column_a(bigint)
RETURNS character AS
'SELECT column_a FROM data_table WHERE id=$1'
LANGUAGE sql IMMUTABLE STRICT;
--Function to get the Column B of the current row
CREATE OR REPLACE FUNCTION get_column_b(bigint)
RETURNS integer AS
'SELECT column_b FROM data_table WHERE id=$1'
LANGUAGE sql IMMUTABLE STRICT;
--Creating a index on expression:
CREATE INDEX meta_sort_index
ON meta_table
USING btree
(get_column_a(id_data), get_column_b(id_data), id_data);
然後我的data_table的Id的複製到meta_table:
INSERT INTO meta_table(id_data) (SELECT id FROM data_table);
後來我可以表增加新行一個類似的簡單插入。
要獲得行900000 - 900099(100行)我現在可以使用:
SELECT get_column_a(id_data), get_column_b(id_data), id_data
FROM meta_table
ORDER BY 1,2,3 OFFSET 900000 LIMIT 100;
(帶一個附加內的data_table JOIN如果我想所有的數據)
生成的計劃是:
Limit (cost=498956.59..499012.03 rows=100 width=8)
-> Index Only Scan using meta_sort_index on meta_table (cost=0.00..554396.21 rows=1000000 width=8)
這是一個非常有效的計劃(索引只掃描是Postgres 9.2中的新增功能)。
但是,如果我想獲得20'000'000 - 20'000'099(100行)的行嗎?同樣的計劃,更長的執行時間。那麼,爲了提高膠印性能(Improving OFFSET performance in PostgreSQL),我可以做以下事情(讓我們假設我每隔第100行將其保存到另一張表中)。
SELECT get_column_a(id_data), get_column_b(id_data), id_data
FROM meta_table
WHERE (get_column_a(id_data), get_column_b(id_data), id_data) >= (get_column_a(587857), get_column_b(587857), 587857)
ORDER BY 1,2,3 LIMIT 100;
這運行得更快。由此產生的計劃是:
Limit (cost=0.51..61.13 rows=100 width=8)
-> Index Only Scan using meta_sort_index on meta_table (cost=0.51..193379.65 rows=318954 width=8)
Index Cond: (ROW((get_column_a(id_data)), (get_column_b(id_data)), id_data) >= ROW('Z'::bpchar, 27857, 587857))
到目前爲止一切正常,postgres做得很好!
我們假設我想將第二列的順序更改爲DESC。
但後來我將不得不改變我的WHERE子句,因爲>運算符比較兩列的ASC。如上(ASC排序)同樣的查詢也可以寫成:
SELECT get_column_a(id_data), get_column_b(id_data), id_data
FROM meta_table
WHERE
(get_column_a(id_data) > get_column_a(587857))
OR (get_column_a(id_data) = get_column_a(587857) AND ((get_column_b(id_data) > get_column_b(587857))
OR ( (get_column_b(id_data) = get_column_b(587857)) AND (id_data >= 587857))))
ORDER BY 1,2,3 LIMIT 100;
現在的計劃變更和查詢速度變慢:
Limit (cost=0.00..1095.94 rows=100 width=8)
-> Index Only Scan using meta_sort_index on meta_table (cost=0.00..1117877.41 rows=102002 width=8)
Filter: (((get_column_a(id_data)) > 'Z'::bpchar) OR (((get_column_a(id_data)) = 'Z'::bpchar) AND (((get_column_b(id_data)) > 27857) OR (((get_column_b(id_data)) = 27857) AND (id_data >= 587857)))))
我如何使用高效的舊方案與DESC-訂購?
你有什麼更好的想法如何解決這個問題?
(我已經嘗試聲明一個自己的類型與自己的操作符表,但是這太慢了)
感謝http://stackoverflow.com/questions/1677538/advanced-indexing-involving-or-ed-conditions-pgsql我試過聯盟。這比上一個計劃好一點,但還不夠。 http://sqlfiddle.com/#!12/9857e/28/3 – Dreamcooled