1

我正在尝试编写一个函数,根据提供的参数有条件地删除数据。我希望能够使用默认参数,这样如果没有明确提供它们,该函数就不会进行任何过滤。两种过滤类型中的第一种是时间范围,第二种是主键数组。

对于时间范围,我可以使用默认的哨兵变量+/- infinity。这匹配所有数据,实质​​上禁用过滤器。这些由 postgres 提供。有什么东西(某种哨兵)可以用来禁用基于数组的过滤器吗?

具体来说,我会向函数传递一个整数数组,但我想默认禁用此过滤器(即,当我不传递数组时)。显而易见的选择是将数组设置为空数组作为默认参数。但是,where in空数组将不匹配任何内容(假设空数组甚至是有效的 plpgql)。所以显而易见的选择是有一个IF语句,或者动态 SQL。但是知道 postgres 必须有一些我可能错过的技巧或语法。

编辑:

我最初的计划是编写一个布尔 UDF 来模拟我想要的行为。类似的东西:

CREATE OR REPLACE FUNCTION filter_category(
    category_array  INTEGER[],
    current_category INTEGER
)
RETURNS 
BOOLEAN AS $$
BEGIN
    IF category_array = '{}' THEN
        RETURN TRUE;
    ELSE
        --  if in set return true, else false. 
        RETURN FALSE;
    END IF;
END;
$$ LANGUAGE plpgsql;

但是,经过对此事的进一步思考,我认为查询将不得不变得更加复杂。一个where in array子句或上面的函数可能不会很有效。因此,要最终使用可能存在的任何索引,我必须取消嵌套主键数组并执行连接。

而且我应该只在数组不为空时才这样做。所以查询将需要至少一个 `IF ... ELSE... END IF' 语句。如果我需要进一步过滤,我可能必须实现中间本地数据集。我仍然对任何建议感兴趣:D

编辑2:

我的动态查询如下所示:

CREATE OR REPLACE FUNCTION get_paginated_search_results(
    forum_id_ INTEGER,
    query_    CHARACTER VARYING,
    from_date_ TIMESTAMP WITHOUT TIME ZONE DEFAULT NULL,
    to_date_ TIMESTAMP WITHOUT TIME ZONE DEFAULT NULL,
    in_categories_ INTEGER[] DEFAULT '{}')
RETURNS SETOF post_result_entry AS $$
DECLARE
    join_string CHARACTER VARYING := ' ';
    from_where_date CHARACTER VARYING := ' ';
    to_where_date CHARACTER VARYING := ' ';
    query_string_ CHARACTER VARYING := ' ';
BEGIN
    IF NOT from_date_ IS NULL THEN
        from_where_date := ' AND fp.posted_at > ''' || from_date_ || '''';
    END IF;

    IF NOT to_date_ IS NULL THEN
        to_where_date := ' AND fp.posted_at < ''' || to_date_ || '''';
    END IF;

    CREATE LOCAL TEMP TABLE un_cat(id) ON COMMIT DROP AS (select * from unnest(in_categories_)) ;

    if in_categories_ != '{}' THEN
        join_string := ' INNER JOIN forum_topics ft ON fp.topic_id = ft.id ' ||
        ' INNER JOIN un_cat uc ON uc.id = ft.category_id ' ;
    END IF;

    query_string_ := '
    SELECT index,posted_at,post_text,name,join_date,quotes
    FROM forum_posts fp
    INNER JOIN forum_user fu ON
    fu.forum_id = fp.forum_id AND fu.id = fp.user_id' ||
        join_string
    ||
    'WHERE fu.forum_id = ' || forum_id_ || ' AND
    to_tsvector(''english'',fp.post_text) @@ to_tsquery(''english'','''|| query_||''')' || 
        from_where_date || 
        to_where_date
    ||';';

    RAISE NOTICE '%', query_string_ ;

    RETURN QUERY
    EXECUTE query_string_;
END;
$$ LANGUAGE plpgsql;
4

1 回答 1

2

您可以使用一个空数组并将其滚动到您的 WHERE 子句中,如下所示:

where ...
  and ($1 = array[]::int[] or id = any ($1))

$1你的int[]参数在哪里。或者您可以使用 NULL 并以相同的方式执行此操作:

where ...
  and ($1 is null or id = any ($1))

不过,我会检查“介于 -infinity 和 +infinity 之间”查询的 EXPLAIN 输出,优化器可能不会将其识别为重言式(假设您没有 NULL,因此它确实是重言式)所以您可以在你手上进行毫无意义的表格扫描。动态 SQL 或一堆丑陋的 IF 可能会更好地为您服务。

于 2012-08-29T04:39:22.120 回答