如何在动态 sql 查询中使用 WITH 块

How to use a WITH block with dynamic sql query

我有一个 plpgsql 函数需要根据用户输入从 3 table 秒开始准备数据,并使用 COPY TO 导出数据。数据是道路交通事故,所以3个table分别是accidentcasualtyvehicle,每个事故链接到车辆和伤亡中的零个或多个记录tables 通过存在于所有三个 tables 中的 accidentid 列。 severitylocal_authorities是输入参数(都是text [])。

sql_query = 'SELECT COUNT(*) FROM accident WHERE severity = ANY(' || quote_literal(severity)
 || ') AND local_auth = ANY (' || quote_literal(local_authorities) || ')';

EXECUTE sql_query INTO result_count;

IF result_count > 0 THEN
   -- replace Select Count(*) With Select *
   sql_query = Overlay(sql_query placing '*' from 8 for 8);
   
   -- copy the accident data first
   EXECUTE 'COPY (' || sql_query || ') TO ' || quote_literal(file_path || file_name_a) ||
     ' CSV';

这第一位会得到相关的事故,所以我现在正在寻找最有效的方法来使用第一个查询中的 accidentid 来下载相关的车辆和伤亡数据。

我想我可以像这样使用 WITH 块:

-- replace * with accidentid
sql_query = Overlay(sql_query placing 'accidentid' from 8 for 1);

WITH acc_ids AS (sql_query)
  EXECUTE 'COPY (SELECT * FROM vehicle WHERE accidentid IN (SELECT accidentid FROM
  acc_ids)) TO ' || out_path_and_vfilename || ' CSV';
  EXECUTE 'COPY (SELECT * FROM casualty WHERE accidentid IN (SELECT accidentid FROM
  acc_ids)) TO ' || out_path_and_cfilename || ' CSV';

但出现错误:

ERROR: syntax error at or near ""

LINE 1: WITH acc_ids AS ( ) EXECUTE 'COPY (SELECT * FROM accident....

我已经在非动态测试用例中尝试了上述方法,例如

WITH acc_ids AS (
    SELECT accidentid FROM accident
    WHERE severity = ANY ('{3,2}')
    AND local_auth = ANY ('{E09000001,E09000002}')
    ) 
    SELECT * FROM vehicle
    WHERE accidentid IN (
    SELECT accidentid FROM acc_ids);

有效。不幸的是服务器仍然运行宁Postgres 8.4所以我暂时不能使用format()

也许 WITH 块不可能做到这一点,但我希望它至少说明了我正在努力实现的目标。

Edit/Update

主要目标是从 3 个 table 的 3 个单独的 csv 文件中获取相关数据,理想情况下无需 运行 在 [=16= 上进行选择] table 3次

如果你想运行一个存储在字符串变量中的查询(部分),你需要一个像

这样的动态查询
EXECUTE 'WITH acc_ids AS (' || sql_query || ')'
   'SELECT ... ';

要么整个查询是由 EXECUTE 执行的字符串,要么整个查询是静态的 SQL。不能混用。

您需要 CTE 吗?如果您可以将查询表示为连接,优化器将有更多选择。

这就是我在没有 CTE 的情况下需要做的事情,但我看不出这是解决这个问题的最有效方法,因为我必须在 accident table 3 上执行相同的查询次数:

sql_query = sql_query || which_tab || ' WHERE severity = ANY ('||
   quote_literal(severity) ||') AND ' || date_start || ' AND ' ||
   date_end || ' AND local_auth = ANY (' ||
   quote_literal(local_authorities) || ')';

-- replace * with COUNT(*)
sql_query = Overlay(sql_query placing 'COUNT(*)' from 8 for 1);
EXECUTE sql_query INTO result_count;

IF result_count > 0 THEN

   -- replace COUNT(*) with *
   sql_query = Overlay(sql_query placing '*' from 8 for 8);
   -- copy the accident data first
   EXECUTE 'COPY (' || sql_query || ') TO ' || quote_literal(file_path ||
      file_name_a) || ' CSV';

   sql_query = Overlay(sql_query placing 'accidentid' from 8 for 1);
   -- vehicles
   EXECUTE 'COPY (SELECT * FROM vehicle WHERE accidentid IN (
    SELECT accidentid FROM accident
    WHERE severity = ANY (' || quote_literal(severity) || ')
    AND local_auth = ANY (' || quote_literal(local_authorities) ||')))
    TO ' || quote_literal(file_path || file_name_v) || ' CSV';
   -- casualties
   EXECUTE 'COPY (SELECT * FROM casualty WHERE accidentid IN (
    SELECT accidentid FROM accident
    WHERE severity = ANY (' || quote_literal(severity) || ')
    AND local_auth = ANY (' || quote_literal(local_authorities) ||')))
    TO ' || quote_literal(file_path || file_name_c) || ' CSV';
END IF;