字符串连接与美元报价

String concatenation with dollar-quoting

提问人:Ivan Vodopyanov 提问时间:8/9/2023 最后编辑:Erwin BrandstetterIvan Vodopyanov 更新时间:8/9/2023 访问量:67

问:

我使用串联来准备一个带有参数值的查询字符串

当我使用单引号时:

p_ReqStr_old := '
with prm as
(
  select
    1::int4 as id, ' || '
    to_timestamp(''' || to_char(p_BegRepDate, 'DD.MM.YYYY') || ''',''DD.MM.YYYY'')::timestamp as p_BegDate,
    to_timestamp(''' || to_char(p_EndRepDate, 'DD.MM.YYYY') || ''',''DD.MM.YYYY'')::timestamp as p_EndDate, ' ||
    cast(p_IsIncludeTestLpu as varchar(1)) || '::int8 as p_IsIncludeTestLpu
)';

我收到以下结果:

with prm as
(
  select
    1::int4 as id, 
    to_timestamp('01.01.2023','DD.MM.YYYY')::timestamp as p_BegDate,
    to_timestamp('31.01.2023','DD.MM.YYYY')::timestamp as p_EndDate, 2::int8 as p_IsIncludeTestLpu
)

当我使用美元报价时:

p_ReqStr_new := $$
with prm as
(
  select
    1::int4 as id,
    to_timestamp(to_char(p_BegRepDate, 'DD.MM.YYYY'), 'DD.MM.YYYY')::timestamp as p_BegDate,
    to_timestamp(to_char(p_EndRepDate, 'DD.MM.YYYY'), 'DD.MM.YYYY')::timestamp as p_EndDate,
    cast(p_IsIncludeTestLpu as varchar(1))::int8 as p_IsIncludeTestLpu
)$$;

我收到这个不良结果:

with prm as
(
  select
    1::int4 as id,
    to_timestamp(to_char(p_BegRepDate, 'DD.MM.YYYY'), 'DD.MM.YYYY')::timestamp as p_BegDate,
    to_timestamp(to_char(p_EndRepDate, 'DD.MM.YYYY'), 'DD.MM.YYYY')::timestamp as p_EndDate,
    cast(p_IsIncludeTestLpu as varchar(1))::int8 as p_IsIncludeTestLpu
)

有没有办法通过美元报价机制接收带有日期值的结果?

PostgreSQL 准备语句 字符串串联 美元引用

评论

0赞 Erwin Brandstetter 8/9/2023
这是针对 PL/pgSQL 函数中的动态 SQL 吗?如果是这样,您可能会让它更简单、更便宜、更安全。输入变量有哪些数据类型?你能透露一个完整的(最小)函数和你的Postgres版本吗?

答:

2赞 jjanes 8/9/2023 #1

当您切换到美元报价时,您还删除了串联。如果你想保持串联,那就保留它。这确实意味着您需要继续关闭和重新打开两边的美元报价,就像使用单引号时一样。切换引号只是意味着您不再需要转义字面上的单引号,它不会改变串联的工作方式。||

也许你应该改用 FORMAT 函数。

评论

0赞 Ivan Vodopyanov 8/10/2023
谢谢你的快速回答。我更新了我在引用和串联方面的知识。是的,它有效!我已将所有单引号替换为$$
2赞 Erwin Brandstetter 8/9/2023 #2

请参阅jjanes关于对美元报价的误解的回答。

假设这是关于 PL/pgSQL 代码块中使用 EXECUTE 的动态 SQL,您不需要任何美元引号(函数体的外部引号除外),也不需要串联。使用子句传递值更便宜、更安全:USING

演示:

DO
$do$
DECLARE
   p_BegRepDate timestamp := '2023-01-01';
   p_EndRepDate timestamp := '2023-01-31';
   p_IsIncludeTestLpu text := '234';        -- ??
BEGIN
   EXECUTE
   $q$  -- no nested ', could just be plane '
   WITH prm AS (
      SELECT 1::int4 AS id
           , $1 AS p_begdate
           , $2 AS p_enddate
           , $3 AS p_is_include_test_lpu
      )
   TABLE prm
   $q$
   USING p_BegRepDate, p_EndRepDate              -- assuming these are type timestamp already
       , (p_IsIncludeTestLpu::varchar(1))::int8  -- can probably be simplified, too
   ;
END
$do$;

相关:

评论

0赞 Ivan Vodopyanov 8/10/2023
谢谢你,欧文,关键词的例子。就我而言,我重新创建了一个动态 SQL 并传入函数。我想我不能用于这个目的。USINGdblink()EXECUTE
1赞 Erwin Brandstetter 8/10/2023
@Ivan:可以执行整个调用,但不能以这种方式将传递到远程查询字符串中。即便如此,您的代码也可以简化一点。你会用必要的细节开始一个新问题......EXECUTE