Postgres动态过滤条件

时间:2020-07-28 13:07:02

标签: postgresql-9.4

我想根据条件动态过滤数据,这些数据存储在特定的列中。此条件对于每一行都会改变。 例如,我有一个带有两列的表 my_table ,其中一列称为 foo ,其中有几个过滤条件,例如 AND bar > 1 或下一行 AND栏> 2 或下一行 AND栏= 33 。 我有一个查询,看起来像:

SELECT something from somewhere 
LEFT JOIN otherthing on some_condition
WHERE first_condition AND second_condition AND 
here_i_want_dynamically_load_condition_from_my_table.foo

正确的方法是什么?我已经阅读了一些有关动态查询的文章,但是找不到正确的方法。

1 个答案:

答案 0 :(得分:0)

这在纯 SQL 中是不可能的:在查询时,规划器必须知道您的确切逻辑。现在,您可以将它隐藏在一个函数中(在伪 sql 中):

CREATE FUNCTION do_I_filter_or_not(some_id) RETURNS boolean AS '
    BEGIN
        value = select some_value from table where id = some_id
        condition_type = SELECT ... --Query a condition type for this row
        condition_value = SELECT ... --Query a condition value for this row
        if condition_type = 'equals' and condition_value = value 
           return true
        if condition_type = 'greater_than' and condition_value < value 
           return true
        if condition_type = 'lower_than' and condition_value > value 
           return true
        return false;
    END;
'
LANGUAGE 'plpgsql';

然后像这样查询:

SELECT something 
FROM somewhere 
LEFT JOIN otherthing on some_condition
WHERE first_condition 
  AND second_condition
  AND do_I_filter_or_not(somewhere.id)

现在性能会很差:您必须在查询的每一行上潜在地调用该函数;触发大量子查询。


考虑一下,如果您只想要 <、>、=,并且您有一个表 (filter_criteria) 描述每个 id 的标准是什么,您可以这样做:

CREATE TABLE filter_criteria AS(
  some_id integer,
  equals_threshold integer,
  greater_than_threshold integer,
  lower_than_threshold integer
  -- plus a check that two thresholds must be null, and one not null
)

INSERT INTO filter_criteria (1, null, 5, null); -- for > 5

然后像这样查询:

SELECT something 
FROM somewhere 
LEFT JOIN otherthing on some_condition
LEFT JOIN filter_criteria USING (some_id)
WHERE first_condition 
  AND second_condition
  AND COALESCE(bar = equals_threshold, true)
  AND COALESCE(bar > greater_than_threshold, true)
  AND COALESCE(bar < lower_than_threshold, true)

如果阈值缺失,COALESCE 在这里默认为不过滤 (AND true)(bar = equals_threshold 将产生 null 而不是布尔值)。

规划器必须在查询时知道您的确切逻辑:现在您只需进行 3 次过滤,每次都使用 =、<、> 检查。对于所有子查询,这仍然比想法 #1 的性能更高。

相关问题