我有一个需要处理单引号的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函数,我该如何使用它?
提前致谢!
答案 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,我认为它不会受到伤害。