【问题标题】:PostgreSQL Error: set-valued function called in context that connot accept a setPostgreSQL 错误:在不接受集合的上下文中调用的集合值函数
【发布时间】:2020-09-29 15:31:26
【问题描述】:

我正在尝试将 T-SQL 过程转换为 PL/PGSQL 过程, 当我运行我的函数时,出现以下错误:“在不能接受集合的上下文中调用的集合值函数”。 没有“返回查询”,我将有一个错误“查询没有结果数据的目的地”,这就是我添加它的原因 在下面找到错误消息和我的功能

create or replace function public.UP_GetCumulPerformancesParPortefeuille(strMatricule VARCHAR(20), strDevise varchar(3), dateDebut DATE) returns setof record 
language plpgsql
as $$
BEGIN

DECLARE NO_PTF_V INT;
        DT_CRS_V DATE;
        PC_PRF_V FLOAT;
        ResultF FLOAT = 0;
        PreviousResult FLOAT = 0;
        PreviousCPA INT = 0;
        Performances public.performancestb;
        PerfCumul public.perfcumultb;
        curseur CURSOR FOR
            SELECT NO_PTF, DT_CRS, SUM(PC_PRF * MT_DEM) 
            FROM (
                SELECT D.NO_PTF, P.DT_CRS, P.PC_PRF
                    , D.MT_DEM/100 as MT_DEM
                FROM public.TB_Demande D
                    INNER JOIN
                    public.performancestb      P ON D.ID_CPA =  P.ID_CPA
                                        AND D.MC_UTL =  strMatricule
            ) Q
            WHERE DT_CRS >= dateDebut
            GROUP BY NO_PTF, DT_CRS
            ORDER BY NO_PTF, DT_CRS;
          
-- Chargement de l'historique des performances
--
BEGIN
INSERT INTO public.performancestb (ID_CPA, DT_CRS, PC_PRF)
SELECT ID_CPA, DT_CRS, PC_PRF FROM public.UF_GetHistoriquePerformances(strDevise);

OPEN curseur;  

FETCH NEXT FROM curseur   
INTO NO_PTF_V, DT_CRS_V, PC_PRF_V; 

WHILE(found) loop
        
    IF PreviousCPA = 0 OR PreviousCPA <> NO_PTF_V then
   
        PreviousResult := 0;
        PreviousCPA := NO_PTF_V;
    end if;
    ResultF := PreviousResult + PC_PRF_V * (PreviousResult + 100);
    PreviousResult := ResultF;

    INSERT INTO public.perfcumultb (NO_PTF, DT_CRS, MT_PRF)
    VALUES (NO_PTF_V, DT_CRS_V, ResultF);
    
    FETCH NEXT FROM curseur   
    INTO NO_PTF_V, DT_CRS_V, PC_PRF_V; 
end loop;
CLOSE curseur;  

Return query(SELECT NO_PTF, DT_CRS, MT_PRF, MT_PRF + 100 as MT_PRF_BSE_100
     , CASE WHEN DT_CRS = FIRST_VALUE(DT_CRS) OVER (PARTITION BY NO_PTF ORDER BY NO_PTF,DT_CRS)
            THEN 0
            ELSE ((100 + MT_PRF) / (100 + LAG(MT_PRF, 1) OVER (PARTITION BY NO_PTF ORDER BY NO_PTF, DT_CRS))) - 1
       END 
       * 100 AS MT_VOL
     , CASE WHEN MT_PRF = FIRST_VALUE(MT_PRF) OVER (PARTITION BY NO_PTF ORDER BY NO_PTF, DT_CRS)
             AND MT_PRF < 0
            THEN MT_PRF
            ELSE ((MT_PRF + 100) / MAX(MT_PRF + 100) OVER (PARTITION BY NO_PTF ORDER BY NO_PTF, DT_CRS 
                                                                ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) - 1 )
                    * 100
       END AS MT_MAX_DDO
  FROM public.perfcumultb);

end;
END;
$$;

【问题讨论】:

    标签: postgresql stored-procedures plpgsql


    【解决方案1】:

    我最终通过创建一个中间表作为返回类型而不是使用 setof record 作为返回类型找到了解决方案。 在下面找到解决方案

    CREATE OR REPLACE FUNCTION public.up_getcumulperformancesparportefeuille(strmatricule character varying, strdevise character varying, datedebut date) returns setof resultfinal
    LANGUAGE plpgsql
    AS $function$
    BEGIN 
    
    DECLARE NO_PTF_V INT;
            DT_CRS_V DATE;
            PC_PRF_V FLOAT;
            ResultF FLOAT = 0;
            PreviousResult FLOAT = 0;
            PreviousCPA INT = 0;
            Performances public.performancestb;
            PerfCumul public.perfcumultb;
            curseur CURSOR FOR
                SELECT NO_PTF, DT_CRS, SUM(PC_PRF * MT_DEM) 
                FROM (
                    SELECT D.NO_PTF, P.DT_CRS, P.PC_PRF
                        , D.MT_DEM/100 as MT_DEM
                    FROM public.TB_Demande D
                        INNER JOIN
                        public.performancestb      P ON D.ID_CPA =  P.ID_CPA
                                            AND D.MC_UTL =  strMatricule
                ) Q
                WHERE DT_CRS >= dateDebut
                GROUP BY NO_PTF, DT_CRS
                ORDER BY NO_PTF, DT_CRS;
              
    -- Chargement de l'historique des performances
    BEGIN
    INSERT INTO public.performancestb (ID_CPA, DT_CRS, PC_PRF)
    SELECT ID_CPA, DT_CRS, PC_PRF FROM public.UF_GetHistoriquePerformances(strDevise);
    
    OPEN curseur;  
    
    FETCH NEXT FROM curseur   
    INTO NO_PTF_V, DT_CRS_V, PC_PRF_V; 
    
    WHILE(found) loop
    
        IF PreviousCPA = 0 OR PreviousCPA <> NO_PTF_V then
     
            PreviousResult := 0;
            PreviousCPA := NO_PTF_V;
        end if;
    
        ResultF := PreviousResult + PC_PRF_V * (PreviousResult + 100);
        PreviousResult := ResultF;
    
        INSERT INTO public.perfcumultb (NO_PTF, DT_CRS, MT_PRF)
        VALUES (NO_PTF_V, DT_CRS_V, ResultF);
        
        FETCH NEXT FROM curseur   
        INTO NO_PTF_V, DT_CRS_V, PC_PRF_V; 
    
    end loop;
    CLOSE curseur;  
    
    return query(SELECT NO_PTF, DT_CRS, MT_PRF, MT_PRF + 100 as MT_PRF_BSE_100
         , CASE WHEN DT_CRS = FIRST_VALUE(DT_CRS) OVER (PARTITION BY NO_PTF ORDER BY NO_PTF,DT_CRS)
                THEN 0
                ELSE ((100 + MT_PRF) / (100 + LAG(MT_PRF, 1) OVER (PARTITION BY NO_PTF ORDER BY NO_PTF, DT_CRS))) - 1
           END 
           * 100 AS MT_VOL
         , CASE WHEN MT_PRF = FIRST_VALUE(MT_PRF) OVER (PARTITION BY NO_PTF ORDER BY NO_PTF, DT_CRS)
                 AND MT_PRF < 0
                THEN MT_PRF
                ELSE ((MT_PRF + 100) / MAX(MT_PRF + 100) OVER (PARTITION BY NO_PTF ORDER BY NO_PTF, DT_CRS 
                                                                    ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) - 1 )
                        * 100
           END AS MT_MAX_DDO
      FROM public.perfcumultb);
    
    end;
    END;
    $function$;
    

    【讨论】:

    • 无需创建中间类型。只需使用returns table (...)
    【解决方案2】:

    一般建议 - 当你来自 MS SQL 世界时,忘掉一切,从阅读 documentation 开始 - 它只有 50 页与 PLpgSQL 相关。在 Postgres 中几乎一切都不同。

    Postgres 不支持未绑定查询——每个查询都应该有一些目标——或者你可以使用PERFORM 语句,它会抛出查询结果。如果要将查询结果推送到输出,则应使用RETURN QUERY 语句。

    RETURN QUERY 的语法是RETURN QUERY query。括号里面是没用的。但是在您的示例中是可见的 MS SQL 模式,该模式未在 Postgres 中使用。在那里(MSSQL)你填写临时表,最后你读了这个表。将此模式移植到 Postgres 是反模式 - 而不是填充辅助表,您应该使用 RETURN NEXT 并立即返回行。也许你需要它(我不知道,因为你在最终查询中使用了窗口函数,但会有很大的开销)。

    您可以使用FOR IN cursor 语句代替手动从游标中获取并使用游标进行操作。

    您的函数返回未定义的记录集,因为您使用了returns setof record。然后你应该用特殊的语法调用这个函数 - SELECT * FROM fx(args) as (colname typename, ...)。最好使用OUT 参数,这样就不需要在查询中指定结果结构。

    CREATE OR REPLACE FUNCTION foo(IN nrows int, OUT a int, OUT b int)
    RETURNS SETOF RECORD AS $$
    BEGIN
      RETURN QUERY SELECT i, i+1 FROM generate_series(1,nrows) g(i);
    END;
    $$ LANGUAGE plpgsql;
    
    SELECT * FROM foo(3);
    ┌───┬───┐
    │ a │ b │
    ╞═══╪═══╡
    │ 1 │ 2 │
    │ 2 │ 3 │
    │ 3 │ 4 │
    └───┴───┘
    (3 rows)
    

    请从阅读文档开始 - T-SQL 确实是不同的语言和环境,迁移到 PLpgSQL 并不直观。

    【讨论】:

    • 对不起,当我写这样的函数时,我没有“a”列名而不是“function_name”:创建或替换函数 public.fnc_list_title(out a text) RETURNS SETOF text LANGUAGE plpgsql AS $ function$ BEGIN ---- RETURN query select n from unnest(array[ 'Project Manager', 'Director' ]) n(a) ;结尾; $函数$ ;
    • @May'Habit select fnc_list_title() 在我的 postgres 中正常工作
    猜你喜欢
    • 2017-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-29
    • 1970-01-01
    • 2019-02-22
    • 2017-03-06
    • 1970-01-01
    相关资源
    最近更新 更多