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

Postgresql 如何在动态sql查询中使用WITH块,postgresql,plpgsql,common-table-expression,dynamic-sql,Postgresql,Plpgsql,Common Table Expression,Dynamic Sql,我有一个plpgsql函数,它需要根据用户输入准备来自3个表的数据,并使用COPY to导出数据。数据是道路事故,因此这3个表是事故、伤亡和车辆,每个事故通过所有三个表中存在的accidentid列链接到车辆和伤亡表中的零个或多个记录。严重性和地方当局都是文本[]的输入参数 这第一位将获得相关的事故,因此我现在正在寻找最有效的方法,使用第一次查询中的accidentid下载相关的车辆和伤亡数据 我想我可以使用这样的块: -- replace * with accidentid sql_query

我有一个plpgsql函数,它需要根据用户输入准备来自3个表的数据,并使用COPY to导出数据。数据是道路事故,因此这3个表是事故、伤亡和车辆,每个事故通过所有三个表中存在的accidentid列链接到车辆和伤亡表中的零个或多个记录。严重性和地方当局都是文本[]的输入参数

这第一位将获得相关的事故,因此我现在正在寻找最有效的方法,使用第一次查询中的accidentid下载相关的车辆和伤亡数据

我想我可以使用这样的块:

-- 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';
但是得到一个错误:

错误:语法错误位于或接近$1

第1行:acc_ID为$1时,执行“从事故中复制选择…”

我在一个非动态测试用例中尝试了上述方法

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,因此我暂时无法使用该格式

也许这在with块中是不可能的,但我希望它至少能说明我正在努力实现的目标

编辑/更新


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

EXECUTE 'WITH acc_ids AS (' || sql_query || ')'
   'SELECT ... ';
要么整个查询是由EXECUTE执行的字符串,要么整个查询是静态SQL。你不能把它们混在一起


你需要CTE吗?如果可以将查询表示为联接,则优化器有更多选项。

如果要运行存储在字符串变量中的查询部分,则需要一个动态查询,如

EXECUTE 'WITH acc_ids AS (' || sql_query || ')'
   'SELECT ... ';
要么整个查询是由EXECUTE执行的字符串,要么整个查询是静态SQL。你不能把它们混在一起


你需要CTE吗?如果可以将查询表示为联接,则优化器有更多选项。

这是我在没有CTE的情况下需要做的事情,但我认为这不是解决此问题的最有效方法,因为我必须对事故表执行相同的查询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;

这就是我在没有CTE的情况下需要做的,但我认为这不是解决这一问题的最有效方法,因为我必须对事故表执行相同的查询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;

将整个查询作为字符串是有意义的,我认为这可能是问题所在。不要认为我可以用连接来做这件事,因为我需要3个单独的导出文件,否则我将不得不重写整个导入例程。事实上,你上面的建议不会起作用,因为我的声明在使用acc_id as is copy select之后。。。不仅仅是选择。鉴于copy语句的文件名和路径也保存在变量中,我几乎需要在第一个execute字符串中嵌套execute“copy select,这在所有的引用中看起来都是不切实际的。编写类似copy with的内容有什么问题吗。。。选择去…?没问题…我的问题措辞不太好。你回答了我问的问题,所以我接受把整个查询作为一个字符串是有意义的,我想这可能就是问题所在。不要认为我可以用连接来做这件事,因为我需要3个单独的导出文件,否则我将不得不重写整个导入例程。事实上,你上面的建议不会起作用,因为我的声明在使用acc_id as is copy select之后。。。不仅仅是选择。鉴于copy语句的文件名和路径也保存在变量中,我几乎需要在第一个execute字符串中嵌套execute“copy select,这在所有的引用中看起来都是不切实际的。编写类似copy with的内容有什么问题吗。。。选择去…?没问题…我的问题措辞不太好。你回答了我的问题,所以我接受