plpgsql - 不同形式的临时表以进行排序

Posted

技术标签:

【中文标题】plpgsql - 不同形式的临时表以进行排序【英文标题】:plpgpsql - differents forms of temporary table in order to do a sort 【发布时间】:2017-03-29 20:24:44 【问题描述】:

我正在尝试优化具有 7 个 WITH 临时表的函数,充当排序机制,从初始临时表/排序级联到下一个直到最后一个,例如第 7 个临时表/排序。

Gist here:这种代码一定要禁止。

我正在尝试将 WITH 排序替换为真正的临时表,例如 CREATE TEMPORARY TABLE <table_name> AS SELECT col1 FROM another_table;。目的是提高性能,因为当前形式的查询非常非常慢。

这是我提出的改变

CREATE OR REPLACE FUNCTION report.get_sa001(
  IN "date_D" timestamp without time zone,
  IN "date_F" timestamp without time zone,
  IN frequence integer)
RETURNS TABLE(
  "Period_date" timestamp without time zone, 
  "Site" character varying, 
  "Customer_code" character varying, 
  "Internal_reference" character varying, 
  "InvoiceNumber" character varying, 
  "Value_in_currency" numeric, 
  "Value_in_EUR" numeric, 
  "Value_Budget_in_EUR" numeric, 
  "Selling_price_CUR" numeric, 
  "Selling_price_EUR" numeric, 
  "Currency_code" character varying, 
  "Selling_quantity" numeric, 
  "Variance_price_CUR" numeric, 
  "Variance_price_EUR" numeric, 
  "Variance_value_CUR" numeric, 
  "Variance_value_EUR" numeric, 
  "Selling_date" timestamp without time zone) AS

$BODY$
DECLARE
  p_debut timestamp without time zone;
DECLARE
  p_fin timestamp without time zone;
BEGIN
  p_debut = dw.get_period_end("date_D", "frequence");
  p_fin = dw.get_period_end("date_F", "frequence");

  RETURN QUERY 
    CREATE TEMPORARY TABLE "dates_1" AS
      SELECT
        p_debut::date + n AS "date",
        dw.period_frequency(p_debut::date + n) AS "frequency"               
      FROM generate_series(0, p_fin::date - p_debut::date) AS x(n)
      WHERE (dw.period_frequency(p_debut::date + n) & frequence != 0);

  SELECT * FROM "dates_1"; -- Thanks to Vao Tsun
END;
$BODY$
  LANGUAGE plpgsql STABLE
  COST 100
  ROWS 1000;

函数的创建很好,但是以这种方式运行函数时

SELECT * FROM report.get_sa001('2017-01-01'::date, '2017-01-31'::date, 32)

这就是我所拥有的

 ERROR: cannot open query CREATE TABLE AS like cursor
 État SQL :42P11
 Contexte : fonction PL/pgsql report.get_sa001(timestamp without time  zone,timestamp without time zone,integer), ligne 11 à RETURN QUERY

我尝试将CREATE TEMPORARY TABLE 替换为SELECT * INTO TEMPORARY TABLE。再次创建正常,但运行时出现相同的错误。

检查 SO 的存档,听起来 PLPGSQL 禁止使用临时表(检查 here)。

如果您有任何想法,我们非常欢迎。

谢谢

【问题讨论】:

你的函数被定义为返回一个表,但它返回 querycreate temporary table... 它不会工作 - 创建表的结果不是返回的列。你需要在末尾添加像SELECT * FROM "Sales_final";这样的东西 嗨@VaoTsun,感谢您的回复。我已经添加了你提到的部分,恐怕不是那样的。 @VaoTsun 实际上是同样的错误 不,不 - 首先是create tabe,然后是return query SELECT * FROM "dates_1";,但我认为列的列表不同,所以它会给你关于错误的信息 @vaotsun 明天晚上 11 点左右我去看看 【参考方案1】:

在函数中使用创建临时表没有任何问题:

t=# create or replace function so37() returns table (i int) as
$$
declare
begin
create temporary table a as select 2;
return query select * from a;
end;
$$ language plpgsql
;
CREATE FUNCTION
t=# select * from so37();
 i
---
 2
(1 row)

但必须维护它们,例如在您当前的示例中,如果存在则缺少删除表,或者您应该插入而不是创建表,因为如果不这样做,第二次运行将失败:

t=# select * from so37();
ERROR:  relation "a" already exists
CONTEXT:  SQL statement "create temporary table a as select 2"
PL/pgSQL function so37() line 4 at SQL statement

而且我相信 CTE 是在函数中创建临时表的更好选择...

【讨论】:

您可以在创建临时表期间使用on commit drop,以便 postgres 在事务结束时删除它,而不是默认 - 会话结束。 使用你必须在每个函数运行之前提交

以上是关于plpgsql - 不同形式的临时表以进行排序的主要内容,如果未能解决你的问题,请参考以下文章

Postgresql:对 plpgsql 中的临时表执行更新不起作用

对于表中的行,将行保存在临时表中以在 plpgsql 的选择查询中使用其数据

如何在 SQL 中创建临时表以用于多个 ADF 活动?

MySQL - 查询临时表以从表中检索 2 行

sql [sql] CTAS - 当您要创建临时表以执行进一步查询时使用。

Oracle新建用户