如何在postgresql函数中处理单引号

时间:2011-11-02 08:44:33

标签: sql function postgresql plpgsql

我有一个需要处理单引号的pg函数。

create or replace function deal_null_value(in user_id numeric) returns integer as
$body$
    declare
        part_num integer;
        sql_str character varying;
    begin
        sql_str := '
        select b.num from (
            select regexp_split_to_table(together,E',\\s*') as together,
            count(id) as num
            from inc_info t
            where
            t.registime>=to_timestamp('2011-10-01 00:00:00','yyyy-MM-dd HH:mi:ss')
            and t.registime<=to_timestamp('2011-10-31 23:59:59','yyyy-MM-dd HH:mi:ss')
            group by together_person
        ) as b 
        where cast(b.together as integer) = ' || user_id;

    EXECUTE sql_str into part_num;
    return part_num;
    end;
$body$
LANGUAGE 'plpgsql'
你知道我有一些单引号。

regexp_split_to_table(together,E',\\s*')
to_timestamp('2011-10-01 00:00:00','yyyy-MM-dd HH:mi:ss')

我想使用$$而不是'而且我想使用quote_literal函数,我该如何使用它?

提前致谢!

2 个答案:

答案 0 :(得分:4)

@Mu已经回答了有关引用的问题,而@araqnid已经解决了投射问题。还有一些问题需要另一个答案。试试这个:

CREATE OR REPLACE FUNCTION deal_null_value(IN user_id numeric, OUT part_num integer)
  AS
$body$
DECLARE
    sql_str  text;
BEGIN
sql_str := $x$
    SELECT b.num FROM (
        SELECT regexp_split_to_table(together, ',') AS together_person
              ,count(*)::integer as num
        FROM   inc_info t
        WHERE  registime >= '2011-10-01 00:00:00'::timestamp
        AND    registime  < '2011-11-01 00:00:00'::timestamp
        GROUP  BY together_person
    ) b 
    WHERE  b.together::numeric = $1
    $x$;

EXECUTE sql_str
INTO    part_num
USING   user_id;

END;
$body$
LANGUAGE 'plpgsql';

主要观点:

  • 这是非法,会引发错误:

    to_timestamp('2011-10-01 00:00:00','yyyy-MM-dd HH:mi:ss')
    

    必须是:

    to_timestamp('2011-10-01 00:00:00','yyyy-MM-dd HH24:mi:ss')
    

    但更好地简化为:

    '2011-10-01 00:00:00'::timestamp
    

    ISO 8601 时间戳格式任何区域设置有效。无需拼出格式,这是标准。

  • 替换

    registime  <= '2011-10-31 23:59:59'::timestamp
    

    registime  < '2011-11-01 00:00:00'::timestamp
    

    时间戳可以有小数秒。你会错过像

    这样的东西
    '2011-10-31 23:59:59.034'::timestamp
    

    ......并且很难找到原因。

  • 简化

    regexp_split_to_table(together,E',\\s*') as together
    

    regexp_split_to_table(together, ',') AS together
    

    另外,我怀疑,你想叫它

    regexp_split_to_table(together, ',') AS together_person
    

    当你进一步分组时。您之后转换为数字(或整数)。 这两个演员阵容中的前导/尾随空格会自动裁剪。因此,不需要转义序列或更复杂的正则表达式。更快,更清洁。

  • count(*)代替count(id)如果 id为非NULL(我假设)。 count(*)更快。

  • 汇总函数count()返回bigint。返回整数时将其强制转换为integer。这里没问题,因为它是自动投射的,但在其他情况下可能会成为一个问题。

  • 使用OUT parameter part_num来简化代码。然后,在分配到part_num后,您不需要明确的RETURN语句。

  • 为什么要将b.together投射到integer?您将其与numeric user_id进行比较?也可以user_id integer或投放到numeric

关于count(*)count(col)

的效果

1)考虑一下:count(*)仅检查是否存在行,而count(col)还必须检查col是否为NULL。 (NULL values don't count!
2)用任何大桌子试试自己:

`EXPLAIN ANALYZE SELECT count(*) from tbl`

`EXPLAIN ANALYZE SELECT count(col) from tbl`.

如果列col已定义NOT NULL知道,则不能有NULL值,则count(*)会产生与{{1}相同的结果}}。在这种情况下使用count(col),速度会快一些。

答案 1 :(得分:2)

Dollar quoting非常简单:只需选择一个令牌(可选),将其放在美元符号($token$)之间,然后像报价一样使用它:

    sql_str := $sql$
    select b.num from (
        select regexp_split_to_table(together,E',\\s*') as together,
        count(id) as num
        from inc_info t
        where
        t.registime>=to_timestamp('2011-10-01 00:00:00','yyyy-MM-dd HH:mi:ss')
        and t.registime<=to_timestamp('2011-10-31 23:59:59','yyyy-MM-dd HH:mi:ss')
        group by together_person
    ) as b 
    where cast(b.together as integer) = $sql$ || user_id;

您可以像使用任何其他字符串函数一样使用quote_literal

    where cast(b.together as integer) = cast($sql$ || quote_literal(user_id) || $sql$ as integer)$sql$;

你的user_id是数字,所以你可能不需要它; OTOH,我认为它不会受到伤害。