gpt4 book ai didi

Postgresql 使用索引对连接表进行排序

转载 作者:行者123 更新时间:2023-11-29 11:28:58 24 4
gpt4 key购买 nike

我目前正在 Postgres 9.2 中处理一个复杂的排序问题您可以在此处找到此问题(简化)中使用的源代码:http://sqlfiddle.com/#!12/9857e/11

我有一个巨大的(>>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 上使用额外的 INNER 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'000 行保存到另一个表中)。

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 做得很好!

假设我想将第 2 列的顺序更改为 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-Ordering 的高效旧计划?
您对如何解决问题有更好的想法吗?

(我已经尝试用自己的运算符类声明自己的类型,但是那太慢了)

最佳答案

您需要重新考虑您的方法。从哪里开始?这是一个明显的示例,基本上说明了您对 SQL 采用的那种函数式方法在性能方面的限制。函数在很大程度上是计划程序不透明的,并且您正在为检索到的每一行强制对 data_table 进行两次不同的查找,因为存储过程的计划不能折叠在一起。

现在,更糟糕的是,您正在根据另一个表中的数据索引一个表。这可能适用于仅附加的工作负载(允许插入但不允许更新),但如果 data_table 可以应用更新,它将起作用。如果 data_table 中的数据发生变化,您将让索引返回错误结果。

在这些情况下,您几乎总是最好将连接写成显式,并让规划器找出检索数据的最佳方式。

现在您的问题是,当您更改第二列的顺序时,您的索引变得不那么有用(并且在磁盘 I/O 方面更加密集)。另一方面,如果您在 data_table 上有两个不同的索引并且有一个显式连接,PostgreSQL 可以更轻松地处理这个问题。

关于Postgresql 使用索引对连接表进行排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17791123/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com