【问题标题】:How to properly insert Timestamp using plpgsql and EXECUTE format...USING dynamic SQL如何使用 plpgsql 和 EXECUTE 格式正确插入时间戳...使用动态 SQL
【发布时间】:2021-12-02 10:36:04
【问题描述】:

我正在构建一个动态 SQL 语句,类似于

my_interval_var := interval - '60 days';

sql_query := format($f$ AND NOT EXISTS (SELECT 1 FROM some_table st WHERE st.%s = %s.id AND st.expired_date >= %L )$f$, var1, var2, now() - my_interval_var)
  1. 关于我的第一个问题,它似乎在now() - my_interval_var 计算之后正确(似乎)插入了时间戳。但是,我只是想确保我不需要强制转换任何东西,因为我可以让它工作的唯一方法是如果我使用%L,它是字符串文字标识符。或者 postgres 是否允许直接与表示时间的字符串进行比较而无需强制转换?比如
some_column <= '2021-12-31 00:00:00'; // is ::timestamp cast needed?
  1. 其次,关于我将 SQL 字符串连接到上面的sql_query 变量,我实际上想跳过我所做的格式,并将这个sql_query 变量直接注入到 EXECUTE...FORMAT ...USING 语句。

我无法让它工作,但是像这样:

EXECUTE format($f$ SELECT * 
FROM %I tbl_alias 
WHERE tbl_alias.%s = %L
%s ) USING var1, var2, var3, sql_query;

是否可以将动态 SQL 标识符 %I %L%s 留在变量中,并在 EXECUTE... 级别对其进行格式化?有人告诉我这是不可能的,但这真的很酷。

  1. 也是我不想添加的最后一个问题,但我觉得有人可能会快速回答。 我用的是]
FOR temprecord IN
   SELECT myCol1, myCol2, myCol3 
   FROM  %I tbl',var1)
   LOOP
    EXECUTE temprecord.someColumnOnMyTbl;
   END LOOP;

...但是当我使查询动态化时,我无法让EXECUTE temprecord.someColumnOnMyTbl 语句工作。我尝试了所有标识符,使用 FORMAT,USING...

我认为列是 %s 之类的字符串,因为我一直对列使用类似 alias.%s = 'some string literal' 的别名时这样做

无论如何,我无法让它工作,我想使列名动态化,但尝试了所有这些方法

EXECUTE format($f$ %I.%s $f$, var1, var2);

EXECUTE format($f$ %$1.%$2 $f$) USING var1, var2;


EXECUTE format($f$ %I.someColumnOn%s $f$, var1, var2);


EXECUTE format($f$ $1.someColumnOn$2 $f$) USING var1, var2;

无论如何,我尝试了更多的东西,但是当我将 temprecord 变量设置为 %I 时,我实际上从数据库中获取了一些数据,但我选择了 3 列,看起来有些东西被第二列抬高了标识符,因为我得到了一个 syntax error,它看起来像是试图连接查询结果的所有 3 列...

我确实尝试过对其进行硬编码并且效果很好...任何帮助表示赞赏!

【问题讨论】:

    标签: postgresql plpgsql dynamic-sql execute


    【解决方案1】:

    字符串文字是未知类型值。 Postgres 总是转换为某些目标二进制格式。类型是从上下文中推断出来的。当您使用函数格式和%L 占位符时,任何二进制值都将转换为字符串,并转义为 Postgres 的字符串文字(防止语法错误和 SQL 注入)。当您使用USING 子句时,二进制值直接传递给执行程序。它有点快,并且在强制转换为字符串的情况下不会丢失一些信息。如果没有这几点,%LUSING 子句的实际效果几乎是一样的。

    您的变量类型是timestampexpired_date 列的类型可能是 date 类型。所以需要对timestamp-&gt;date进行一些转换。

    函数format 只是字符串函数。它只是制作字符串。为了更好的可读性,它支持占位符,以确保正确的转义和正确的结果 SQL 字符串。 %L 与调用函数 quote_literal 相同,%Iquote_ident 相同(对于列、表名)。 %s 插入字符串而不转义和引用。 format 函数的结果(当你在EXECUTE 命令中使用它时)应该是有效的SQL 语句。您可以在RAISE NOTICE 命令中使用它,并且可以将结果打印到调试输出。通常这是个好主意

    DECLARE
      query text;
      x date DEFAULT current_date
      y int;
    BEGIN
      query := format('.... WHERE inserted = $1', ...);
      RAISE NOTICE 'dynamic query will be: %', query);
      EXECUTE query USING x INTO y;
      ...
    

    子句USING 允许在动态SQL 中使用参数(EXECUTE 子句)。通常,format 的占位符应该用于表名或列名,USING 用于其他任何名称。

    对于类型 datetimestamp(标量基本类型),以下执行将在 99.99% 上相同:

    EXECUTE format('select count(*) from foo where inserted = %L', current_date) INTO ..
    
    EXECUTE 'select count(*) from foo where inserted = $1' USING current_date INTO ..
    

    您不能在列名或表名位置上使用查询参数。这是USING 子句的限制。但对于任何其他情况,此子句应用作主要的。

    【讨论】:

      猜你喜欢
      • 2015-01-09
      • 2017-12-26
      • 2022-01-25
      • 2015-09-08
      • 2011-01-19
      • 2019-05-25
      • 1970-01-01
      • 1970-01-01
      • 2015-08-25
      相关资源
      最近更新 更多