触发以查找下一个可用的库存位置
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;
有几种方法可以做到这一点。您可以将列 AVAILABLE
或 OCCUPIED
添加到第一个 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.
中随机取行
我正在尝试实施库存跟踪,但 运行 遇到了问题。由于这是我第一次涉足数据库触发器(& 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;
有几种方法可以做到这一点。您可以将列 AVAILABLE
或 OCCUPIED
添加到第一个 table
select 数据仅来自 table 和 where available = 'Y'
。在这种情况下,您还需要触发器
用于在第二个 table.
第二个选项 - 当插入数据时使用 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.