带有 CASE 语句的 PostgreSQL 慢连接

PostgreSQL slow JOIN with CASE statement

在我的数据库中,我有一个 table,其中包含约 3500 条记录,作为更复杂查询的一部分,我尝试使用 "CASE" 条件对自身执行内部联接,就像您可以的那样见下文。

SELECT *
FROM some_table AS t1
JOIN some_table AS t2 ON t1.type = t2.type
    AND CASE
       WHEN t1.type = 'ab' THEN t1.first = t2.first
       WHEN t1.type = 'cd' THEN t1.second = t2.second
       -- Column type contains only one of 2 possible varchar values
    END;

问题是这个查询执行了 3.2 - 4.5 秒,而下一个请求执行了 40 - 50 毫秒。

SELECT *
FROM some_table AS t1
JOIN some_table AS t2 ON t1.type = t2.type
    AND (t1.first = t2.first OR t1.second = t2.second)

同样根据第一种情况的执行计划,数据库处理了约 580 万条记录,而 table 仅包含约 3500 条记录。 table 上还有下一个索引:(id), (type), (type, first), (type, second)。

我们正在使用下一个版本: x86_64-unknown-linux-gnu 上的 PostgreSQL 9.4.5,由 gcc (GCC) 4.4.7 20120 编译 313(红帽 4.4.7-16),64 位

知道为什么 PostgreSQL 在这种情况下工作如此奇怪吗?

测试这个:

select *
from
    some_table as t1
    join
    some_table as t2 on
        t1.type = t2.type
        and
        (
            t1.type = 'ab' and t1.first = t2.first
            or
            t1.type = 'cd' and t1.second = t2.second
        )

为了更好的性能,创建一个基于函数的索引:

create or replace function f (_type text, _first int, _second int)
returns integer as $$
    select case _type when 'ab' then _first else _second end;
$$ language sql immutable;

create index i on some_table(f(type, first, second));

在查询中使用该索引:

select *
from
    some_table as t1
    join
    some_table as t2 on
        t1.type = t2.type
        and
        f(t1.type, t1.first, t1.second) = f(t1.type, t2.first, t2.second)