Sql 如果参数为NULL,如何从WHERE子句中删除条件

Sql 如果参数为NULL,如何从WHERE子句中删除条件,sql,postgresql,null,plpgsql,Sql,Postgresql,Null,Plpgsql,我将向PL/pgSQL函数传递2个参数。问题是: SELECT * FROM table WHERE col1 = param1 AND col2 = param2 这两个参数都可以为NULL,在这种情况下,应该从WHERE子句中删除相应的表达式 我该怎么做?使用IF条件?最简单(但可能不是最有效)的方法是在SQL语句本身内部处理null,例如: SELECT * FROM table WHERE col1 = param1 AND (param2 IS NULL OR col

我将向PL/pgSQL函数传递2个参数。问题是:

SELECT * 
FROM table 
WHERE col1 = param1 
  AND col2 = param2
这两个参数都可以为NULL,在这种情况下,应该从
WHERE
子句中删除相应的表达式

我该怎么做?使用
IF
条件?

最简单(但可能不是最有效)的方法是在SQL语句本身内部处理null,例如:

SELECT * 
FROM table 
WHERE col1 = param1 
AND (param2 IS NULL OR col2 = param2)
如果参数为null,则此函数将为所有行传递子句

或者用这个技巧:

SELECT * 
FROM table 
WHERE col1 = param1 
AND col2 = COALESCE(param2, col2)
param2
NULL
时,该条件相当于
col2=col2
,只要
col2
本身不包含
NULL
值,则该条件始终为真


使用硬编码值而不是函数参数的快速测试为这两种方法提供了相同的查询计划-在规划查询之前,
合并
部分似乎已经过优化,因此,第二个
似乎确实被有条件地删除了。

也许这是在耍把戏:

SELECT * 
FROM table 
WHERE col1 = param1 
  AND (param2 is null or col2 = param2);
这并不是删除AND条件,而是在param2为null的情况下使AND条件变得不重要。所以不清楚地回答你的问题,而是四处走动……;)

一个简单的
(param1为NULL或col1=param1)
解决了这个问题,前面已经回答过了

实际删除任何或所有空条件,您需要动态SQL。您可以有条件地在客户机中构建语句,也可以创建plpgsql(或任何其他过程语言)函数来处理语句。在处理复杂查询时,这种方法可能会产生更好的查询计划

功能 棘手的部分是:

  • 在字符串连接中正确处理空值和空字符串
  • 处理可变数据类型,同时避免可能的SQL注入


为此,您需要对plpgsql有基本的了解。您会在标签中找到大量示例,并进行了充分的解释。

该函数是用
plpgsql
编写的(与普通SQL或其他可插入语言相反)+1但请注意,coalesce不是一个好主意,因为如果有索引可用,它将不使用索引,而第一个变体将使用。@Denis我只是用硬编码的
NULL
或字符串代替
param2
,对这两种策略进行了测试,得到了相同的查询计划。由于它对所有行都是常量,第一行变为
TRUE或…
FALSE或…
,可以进行优化,第二行变为
合并(NULL,col2)
合并(“已知值”,col2)
,在计划查询之前,可以用
col2
已知值“
替换。实际上,
col2=col2
并不总是正确的。如果
col2
可能包含空值,则此条件可能是
unknown
,因此此方法不仅不会使用索引,而且可能返回错误results@RomanPekar关于
col2
的空值的好观点;关于这一点,我将添加一个注释。第一个查询更适合在PL/pgSQL中使用-规划人员将创建两个不同的计划,并在执行时选择一个。我修改了这个问题。有人也可以回答这个问题。应用您在这一次中所学到的知识
其中(param1为null或col1=param1)和(param2为null或col2=param2)
。当然,如果两者都为null,那么这将返回所有内容。也许在这一点上考虑有效的默认值?
CREATE OR REPLACE FUNCTION f_conditional_where(_param1 int  = NULL
                                             , _param2 text = NULL
                                             , _param3 date = NULL)
  RETURNS SETOF tbl AS
$func$
DECLARE
   _where text :=
      concat_ws(' AND '
        , CASE WHEN _param1 IS NOT NULL THEN 'col1 = $1' END
        , CASE WHEN _param2 IS NOT NULL THEN 'col2 = $2' END
        , CASE WHEN _param3 IS NOT NULL THEN 'col3 = $3' END);   
   _sql text := 'SELECT * FROM tbl';
BEGIN

IF _where <> '' THEN
   _sql := _sql || ' WHERE ' || _where;
END IF;

-- debug output
RAISE NOTICE '
 _sql:   |%|
 _where: |%|', _sql, _where;

-- execute
RETURN QUERY EXECUTE _sql
USING $1, $2, $3;

END
$func$  LANGUAGE plpgsql;
SELECT * FROM f_conditional_where();
SELECT * FROM f_conditional_where(1, 'foo');
SELECT * FROM f_conditional_where(_param3 := '2012-01-01', _param2 := 'foo');