SQL技巧:实现“先进先出”

亲爱的读者们,今天和大家分享”先进先出”案例。”先进先出”(FIFO)原则在仓库存储和库存管理中广泛应用。该原则假设最早进入仓库的商品或材料应当是第一个被出售或使用的。

在计算库存成本时,这种方法更准确地反映了货物的流动情况。具体实施方式如下:

1. 当一个新的产品批次到达时,它被放在库存的后面。

2. 当需要从库存中取出产品时,则优先取出最早入库的批次。

本次使用的是PostgreSQL数据库实现,话不多说,代码如下:

DO LANGUAGE 'plpgsql' -- 指定匿名块的语言,可省略默认为plpgsql
$BODY$
DECLARE
v_TABEXT int         := 1;            --用于判断流出表中是否还有数据
v_liuchu int         := 1;            --流出顺序
v_liuru  int         := 1;            --流入顺序
v_budat  varchar(10) := '2023-10-01'; --结账日期
BEGIN
--开始循环
WHILE v_TABEXT <> 0 loop
--循环体语句
--创建流出临时表定义,存储根基工厂、供应商、物料号、物料凭证、凭证日期升序的临时数据
DROP TABLE if EXISTS table_out_tmp
;

CREATE TEMPORARY TABLE table_out_tmp
 (
  mblnr varchar(20NULL
   , --物料凭证号
  mjahr numeric(4NULL
   , --物料凭证年
  zeile numeric(4NULL
   , --行项目
  budat timestamp NULL
   , --流出过账日期
  bwart varchar(6NULL
   , --移动类型
  matnr varchar(36NULL
   , --物料号
  werks varchar(8NULL
   , --工厂
  kunnr varchar(20NULL
   , --客户
  lifnr varchar(20NULL
   , --供应商
  menge numeric(13,3NULL
   , --数量
  meins varchar(6NULL
   , --单位
  menge_total_out numeric(13,3NULL
   ,                  --累计流出
  out_sequence int --流出顺序
 )
on
 commit preserve rows
;

-- 创建流入临时表定义,存储根基工厂、供应商、物料号、物料凭证、凭证日期升序的临时数据
DROP TABLE if EXISTS table_in_tmp
;

CREATE TEMPORARY TABLE table_in_tmp
 (
  mblnr varchar(20NULL
   , --物料凭证号
  mjahr numeric(4NULL
   , --物料凭证年
  zeile numeric(4NULL
   , --行项目
  budat timestamp NULL
   , --流出过账日期
  bwart varchar(6NULL
   , --移动类型
  matnr varchar(36NULL
   , --物料号
  werks varchar(8NULL
   , --工厂
  kunnr varchar(20NULL
   , --客户
  lifnr varchar(20NULL
   , --供应商
  menge numeric(13,3NULL
   , --数量
  meins varchar(6NULL
   , --单位
  zlldh varchar(20NULL
   , --领料单号
  menge_total_in numeric(13,3NULL
   ,                 --累计流入
  in_sequence int --流入顺序
 )
on
 commit preserve rows
;

--将流出数据插入临时表
INSERT INTO table_out_tmp
SELECT
 MBLNR
  , MJAHR
  , ZEILE
  , budat
  , bwart
  , matnr
  , werks
  , kunnr
  , lifnr
  , menge
  , meins
  , SUM(menge) OVER (PARTITION BY werks,lifnr,matnr ORDER BY
      budat,MBLNR ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROWas menge_total_out
  , ROW_NUMBER()OVER (PARTITION BY werks,lifnr,matnr ORDER BY
       budat,MBLNR ) as out_sequence
FROM
 table1
WHERE
 budat    >= to_date(v_budat,'YYYY-MM-dd')
 AND menge < 0
 --AND matnr ='000000001030013291'
 AND NOT EXISTS
 (
  SELECT *
  FROM
   table_target
  WHERE
   table1.mblnr = table_target.mblnr
   AND in_menge            >=0
 )
;

--将流入数据插入临时表
INSERT INTO table_in_tmp
SELECT
 table1.MBLNR
  , table1.MJAHR
  , table1.ZEILE
  , table1.budat
  , table1.bwart
  , table1.matnr
  , table1.werks
  , table1.kunnr
  , table1.lifnr
  , COALESCE(in_menge,table1.menge) AS menge
  , table1.meins
  , table1.zlldh
  , SUM(table1.menge) OVER (PARTITION BY table1.werks,table1.lifnr,table1.matnr ORDER BY
          table1.budat,table1.MBLNR ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROWas menge_total_in
  , ROW_NUMBER()OVER (PARTITION BY table1.werks,table1.lifnr,table1.matnr ORDER BY
       table1.budat,table1.MBLNR ) as in_sequence
FROM
 table1
 LEFT JOIN
  (
   SELECT
    in_mblnr
     , in_mjahr
     , in_zeile
     , in_budat
     , in_zlldh
     , min(in_menge) AS in_menge
   FROM
    table_target
   GROUP BY
    in_mblnr
     , in_mjahr
     , in_zeile
     , in_budat
     , in_zlldh
  )
  AS table_target
  ON
   (
    table_target.in_mblnr     = table1.mblnr
    AND table_target.in_mjahr = table1.mjahr
    AND table_target.in_zeile = table1.zeile
    AND table_target.in_budat = table1.budat
    AND table_target.in_zlldh = table1.zlldh
   )
WHERE
 table1.budat                           >= to_date(v_budat,'YYYY-MM-dd')
 AND table1.menge                        > 0
 AND COALESCE (table_target.in_menge,0) >= 0
 AND NOT EXISTS
 (
  SELECT *
  FROM
   table_target
  WHERE
   table1.mblnr              = table_target.mblnr
   AND table_target.in_menge < 0
 )
;

--根据流出工厂、物料、供应商匹配流入时的凭证号、凭证年、行项目、领料单号
INSERT INTO table_target
 (
  SELECT
   table_out_tmp.MBLNR
    , table_out_tmp.MJAHR
    , table_out_tmp.ZEILE
    , table_out_tmp.budat
    , table_out_tmp.bwart
    , table_out_tmp.matnr
    , table_out_tmp.werks
    , table_out_tmp.kunnr
    , table_out_tmp.lifnr
    , table_out_tmp.menge
    , table_out_tmp.meins
    , table_in_tmp.MBLNR
    , table_in_tmp.MJAHR
    , table_in_tmp.ZEILE
    , table_in_tmp.budat
    , table_in_tmp.zlldh
    , table_in_tmp.menge + table_out_tmp.menge AS in_menge
    , now()                                                               load_date
  FROM
   table_out_tmp
   LEFT JOIN
    table_in_tmp
    ON
     table_out_tmp.werks     = table_in_tmp.werks
     AND table_out_tmp.lifnr = table_in_tmp.lifnr
     AND table_out_tmp.matnr = table_in_tmp.matnr
  WHERE
   table_out_tmp.out_sequence   = v_liuchu
   AND table_in_tmp.IN_sequence = v_liuru
 )
;

--COMMIT;
--判断流出
SELECT
 count(*)
INTO
 v_TABEXT
FROM
 table_out_tmp
;

end loop;
END
$BODY$;

难点:

每一笔的出库数和进库数不是一一对应的。比如:订单A 1号进库数量为500,但是出库数量可以小于、等于、大于500,所以每次计算都需要库里剩余量和下次出库量作比较。

知识点:本次使用SQL实现”先进先出”原则,主要用到之前分享过窗口函数以及存储过程技术。

以下是采用FIFO策略的一些好处:

  • 降低过期风险: 对于有保质期或易腐烂的商品特别重要,因为FIFO保证了最早入库的商品会被优先销售。

  • 减少损耗: 长时间存储可能会导致一些商品的质量下降,通过FIFO可以尽快售出,从而降低损耗。

  • 估值更准确: 在通货膨胀环境下,因为最早购买的库存成本较低,所以利润会被高估。这也更接近现实商品的流动情况。

然而,FIFO策略并不适用所有情况,比如对于无法移动或者储存需求特殊的商品,可能需要使用其他库存管理策略。

希望您喜欢今天的分享,数据处理总是充满挑战,但只要我们掌握了正确的工具和技巧,就能以更有效率的方式解决问题。记得关注我们哟,获取更多小知识。


原文始发于微信公众号(运维小九九):SQL技巧:实现“先进先出”

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/218585.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!