在Postgresql的视图中使用存储过程

时间:2013-10-17 14:47:59

标签: sql postgresql stored-procedures plpgsql correlated-subquery

我一直在尝试创建一个View,其中一个pending_amount列由于存储过程执行而获取其值。

存储过程是 pending_stock(int,int)并返回一个整数。视图已成功创建,但是当我尝试在此视图上执行任何查询时,只需返回一个值。

CREATE OR REPLACE VIEW view_production_parts AS 
SELECT p.part_id, p.min_amount, gp.part_num, gp.description
     , p.quantity_available
     , p.quantity_total - p.quantity_available AS quantity_alloc
     , p.quantity_total
     , (SELECT pending_stock(p.part_id, 0) AS pending_stock) AS pending_amount
     , p.production_run
     , CASE
           WHEN ppur.purchased_part_id IS NOT NULL THEN true
           ELSE false
       END AS is_purchased_part, ppur.purchased_part_id, p.store_move_type_id
     , gp.part_status_id, p.default_location
     , COALESCE(pwoh.part_work_order_hold_id, 0) AS part_work_order_hold_id
   FROM general_part gp
   JOIN part p ON gp.part_id = p.part_id
   LEFT JOIN purchased_part ppur ON ppur.part_id = p.part_id
   LEFT JOIN part_work_order_hold pwoh ON pwoh.part_id = p.part_id
  ORDER BY gp.part_num;

可以在视图中使用存储过程吗?如果使用,我的声明是否正确?

找到result from of this query at explain.depesz.com

EXPLAIN ANALYZE SELECT count(*) FROM view_production_parts

我正在使用Postgres 8.4 pending_stock(int,int)的函数定义:

CREATE OR REPLACE FUNCTION pending_stock(var_part_id integer
                                       , var_pattern_id integer)
  RETURNS integer AS
$BODY$
declare
    r record;
    var_qty_expected integer;
    var_qty_moved_to_stock integer;
    var_total_stock_moved_out integer;
    var_actual_qty integer;

begin

var_total_stock_moved_out := 0;
var_qty_expected := 0;

   for r in
      select work_order_id,quantity_expected
      from view_work_orders
      where part_id = var_part_id and open = 'TRUE'
      and quantity_allocated is null and quantity_expected >= quantity_actual

   loop
      var_qty_expected = var_qty_expected + r.quantity_expected;

      select sum(quantity) from view_work_order_move_parts_details
      where source_work_order_id = r.work_order_id
      and part_id = var_part_id into var_qty_moved_to_stock;

      if var_qty_moved_to_stock is null then
         var_qty_moved_to_stock = 0;
      end if;

      var_total_stock_moved_out = var_total_stock_moved_out
                                + var_qty_moved_to_stock;
   end loop;

   var_actual_qty := var_qty_expected - var_total_stock_moved_out;

   if var_actual_qty > 0 then
      return var_actual_qty;
   else
      return 0;
   end if;
end;
$BODY$
LANGUAGE 'plpgsql' VOLATILE STRICT
COST 100;
ALTER FUNCTION pending_stock(integer, integer) OWNER TO postgres;

1 个答案:

答案 0 :(得分:1)

视图

您不需要函数调用的子查询。您可以简化其他一些细节:

CREATE OR REPLACE VIEW view_production_parts AS 
SELECT  p.part_id, p.min_amount
      , gp.part_num, gp.description, p.quantity_available
      , p.quantity_total - p.quantity_available AS quantity_alloc
      , p.quantity_total
      , pending_stock(gp.part_id, 0) AS pending_amount
      , p.production_run
      ,(ppur.purchased_part_id IS NOT NULL) AS is_purchased_part
      , ppur.purchased_part_id, p.store_move_type_id, gp.part_status_id
      , p.default_location
      , COALESCE(pwoh.part_work_order_hold_id, 0) AS part_work_order_hold_id
FROM    general_part              gp
JOIN    part                      p    USING (part_id)
LEFT    JOIN purchased_part       ppur USING (part_id)
LEFT    JOIN part_work_order_hold pwoh USING (part_id)
ORDER   BY gp.part_num;

除此之外,VIEW定义看起来很好。

功能

可以大大简化:

CREATE OR REPLACE FUNCTION pending_stock(var_part_id integer
                                       , var_pattern_id integer)
  RETURNS integer AS
$func$
DECLARE
    r record;
    var_qty_expected            integer   := 0;
    var_total_stock_moved_out   integer   := 0;
BEGIN
   FOR r IN
      SELECT work_order_id, quantity_expected
      FROM   view_work_orders
      WHERE  part_id = var_part_id
      AND    open = 'TRUE'  -- A string instead of a boolean?
      AND    quantity_allocated IS NULL
      AND    quantity_expected >= quantity_actual
   LOOP
      var_qty_expected := var_qty_expected + r.quantity_expected;

      SELECT var_total_stock_moved_out + COALESCE(sum(quantity), 0)
      FROM   view_work_order_move_parts_details
      WHERE  source_work_order_id = r.work_order_id
      AND    part_id = var_part_id
      INTO   var_total_stock_moved_out;
   END LOOP;

   RETURN GREATEST(var_qty_expected - var_total_stock_moved_out, 0);
END
$func$ LANGUAGE plpgsql

重点

  • 通常,plpgsql中的赋值相对较贵。每个赋值都在内部执行(非常简单快速)SELECT语句。尽量少用它们。

  • 您可以在声明时初始化变量。无需另外发表声明。

  • The assignment operator in plpgsql is :==有效,但没有记录。

  • 使用COALESCE()来捕获NULL值。

  • 从不使用函数参数var_pattern_id。这可能不是完整的功能定义。

  • 使用GREATEST

  • 可以用一个语句替换整个最终部分

高级查询

现在,这个清理过的功能会快一些,但不会太多。你反复循环的整个设计是非常低效。它导致相关的子查询再次循环通过相关的子查询。表演噩梦。

将问题重新设置为基于集合的操作,以使其更快。好吧, 很多 更快。

SELECT e.part_id
      ,GREATEST(COALESCE(sum(e.quantity_expected), 0)
              - COALESCE(sum(m.total_stock_moved_out), 0), 0)
FROM   view_work_orders e
LEFT   JOIN (
   SELECT source_work_order_id       AS work_order_id
         ,COALESCE(sum(quantity), 0) AS total_stock_moved_out
   FROM   view_work_order_move_parts_details
   WHERE  part_id = var_part_id
   GROUP  BY 1
   ) m USING (work_order_id)
WHERE  e.part_id            =  var_part_id
AND    e.open               =  'TRUE'
AND    e.quantity_allocated IS NULL
AND    e.quantity_expected  >= e.quantity_actual
GROUP  BY 1;

高级视图

将其整合到原始查询/视图中:

CREATE OR REPLACE VIEW view_production_parts AS 
SELECT p.part_id, p.min_amount
      ,gp.part_num, gp.description, p.quantity_available
      ,p.quantity_total - p.quantity_available AS quantity_alloc
      ,p.quantity_total
      ,x.pending_amount
      ,p.production_run
      ,(ppur.purchased_part_id IS NOT NULL) AS is_purchased_part
      ,ppur.purchased_part_id, p.store_move_type_id, gp.part_status_id
      ,p.default_location
      ,COALESCE(pwoh.part_work_order_hold_id, 0) AS part_work_order_hold_id
FROM   general_part              gp
JOIN   part                      p    USING (part_id)
LEFT   JOIN purchased_part       ppur USING (part_id)
LEFT   JOIN part_work_order_hold pwoh USING (part_id)
LEFT   JOIN (
   SELECT e.part_id
         ,GREATEST(COALESCE(sum(e.quantity_expected), 0)
                 - COALESCE(sum(m.total_stock_moved_out), 0)
                   , 0) AS pending_amount
   FROM   view_work_orders e
   LEFT   JOIN (
      SELECT source_work_order_id AS work_order_id
            ,sum(quantity)        AS total_stock_moved_out
      FROM   view_work_order_move_parts_details
      WHERE  part_id = var_part_id
      GROUP  BY 1
      ) m USING (work_order_id)
   WHERE  e.part_id            =  var_part_id
   AND    e.open               =  'TRUE'
   AND    e.quantity_allocated IS NULL
   AND    e.quantity_expected  >= e.quantity_actual
   GROUP  BY 1
   ) x USING (part_id)
ORDER  BY gp.part_num;

显然未经测试。