触发以查找下一个可用的库存位置

Trigger to find next available inventory location

我正在尝试实施库存跟踪,但 运行 遇到了问题。由于这是我第一次涉足数据库触发器(& PL/SQL 一般),我认为我需要调整我的 thinking/understanding 如何解决这个问题。 我的情况如下:每次将新项目添加到我的库存时,我都需要自动为其分配第一个可用的物理存储位置。当物品被消耗时,它们会从库存中移除,从而释放一个物理位置(即我们正在回收这些物理位置)。我有两个 table:一个库存 table 和一个 table 包含所有合法位置 names/Ids。

Table: ALL_LOCATIONS
Location_ID
SP.1.1.1.a
SP.1.1.1.b
SP.1.1.1.c
SP.1.1.2.a
SP.1.1.2.b
SP.1.1.2.c
SP.1.1.3.a
SP.1.1.3.b
SP.1.1.3.c
...
SP.25.5.6.c

Table: ITEM_INVENTORY    
Item_ID | Location_ID
    1         SP.1.1.1.a
    2         SP.1.1.1.b
    4         SP.1.1.2.a
    5         SP.1.1.2.b
    6         SP.1.1.2.c
    21        SP.1.1.4.a
    …         …

Note: First available location_ID should be SP.1.1.1.c

我需要创建一个触发器,将下一个可用 Location_ID 分配给插入的行。搜索此站点时,我看到了几个类似的问题,但它们都是针对确定下一个可用位置的逻辑。就我而言,我想我已经记下了,但我不知道如何将其作为触发器来实现。让我们只关注插入触发器。 "MINUS" 策略(如下所示)在选择下一个可用位置方面效果很好,但 Oracle 不喜欢在触发器内使用此策略,因为我正在读取与我正在编辑的 table 相同的格式(抛出一个变异table 错误)。

我阅读了一些关于变异 table 错误的文章,并提出了一些解决方法(自主事务等),但是,我阅读的关键信息是,"you're going about it the wrong way." 所以我的问题是, "what's another way of approaching this problem so that I can implement a clean & simple solution without having to hack my way around mutating tables?"

注意:我相信您会发现我的触发代码中有很多不完全正确的地方,如果您指出它们,我一定会学到一些东西——但是我的目标是学习新的方法来 approach/think关于我设计的根本问题。

create or replace TRIGGER Assign_Plate_Location
  BEFORE INSERT ON ITEM_INVENTORY
  FOR EACH ROW

  DECLARE
  loc VARCHAR(100) := NULL;
  
  BEGIN
  IF(:new.LOCATION_ID IS NULL) THEN
      BEGIN 
        SELECT LOCATION_ID INTO loc FROM
          (SELECT DISTINCT LOCATION_ID FROM ALL_LOCATIONS
          MINUS
          SELECT DISTINCT LOCATION_ID FROM ITEM_INVENTORY)
        WHERE ROWNUM = 1;
      EXCEPTION
      WHEN NO_DATA_FOUND THEN
        loc := NULL;
      END;
      
      IF(loc IS NOT NULL) THEN
        :new.LOCATION_ID := loc;
      END IF;
    END IF;
  END;

有几种方法可以做到这一点。您可以将列 AVAILABLEOCCUPIED 添加到第一个 table select 数据仅来自 table 和 where available = 'Y'。在这种情况下,您还需要触发器 用于在第二个 table.

上删除和更新 location_id

第二个选项 - 当插入数据时使用 merge 或当 item_inventory.location_id 为空时从 all_locations 检索数据的一些过程。

第三个选项 - 引入了 Oracle 11g compound triggers 这允许更好地处理变异 tables。在这种情况下,触发器看起来像这样:

create or replace trigger assign_plate_location
for insert on item_inventory compound trigger

  loc varchar2(15) := null;
  type t_locs is table of item_inventory.location_id%type;
  v_locs t_locs;
  i number := 1;

  before statement is 
  begin
    select location_id 
      bulk collect into v_locs from all_locations al
      where not exists (
        select location_id from item_inventory ii 
          where ii.location_id = al.location_id );
  end before statement;

  before each row is
  begin
    if :new.location_id is null then
      if i <= v_locs.count() then 
        :new.location_id := v_locs(i);
        i := i + 1;
      end if;
    end if;
  end before each row;
end assign_plate_location;

我根据您示例中的数据对其进行了测试,插入(select)看起来不错。你可以试一试,看看它是否有效,也许这会适合你。

最后的注意事项 - 在您的 select 中,您不需要 distinct,MINUS makes values distinct。 还要考虑排序数据,现在您的 select(和我的)可能会从 ALL_LOCATIONS.

中随机取行