我正在尝试实施库存跟踪并遇到问题。由于这是我第一次涉足数据库触发器(& PL / SQL),我想我需要调整一下我对如何解决这个问题的思考/理解。 我的情况如下:每次将新项目添加到我的库存时,我需要自动为其分配第一个可用的物理存储位置。当物品被消耗时,它们将从库存中移除,从而释放物理位置(即,我们正在回收这些物理位置)。我有两个表:一个库存表和一个包含所有合法位置名称/ ID的表。
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
… …
注意:第一个可用的location_ID应为SP.1.1.1.c
我需要创建一个触发器,将下一个可用的Location_ID分配给插入的行。搜索这个网站我看到了几个类似的问题,但是它们是针对确定下一个可用位置的逻辑。就我而言,我认为我已经失败了,但我不知道如何将其作为触发器来实现。我们只关注插入触发器。 “MINUS”策略(如下所示)在选择下一个可用位置时效果很好,但Oracle在触发器中不喜欢这个,因为我正在读取与我正在编辑的表相同的表(抛出变异表错误)。
我已经完成了关于变异表错误的一些阅读,并提出了一些变通方法(自治事务等)。但是,我读到的关键信息是“你的方式错误”。所以我的问题是,“解决这个问题的另一种方法是什么,以便我可以实现一个干净简单的解决方案,而不必破解我的方式来改变表格?”
注意:我确信您可以使用我的触发器代码找到各种不太正确的方式,如果您指出它们,我肯定会学到一些东西 - 但我的目标是学习接近/思考的新方法关于我的设计的根本问题。
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;
答案 0 :(得分:1)
有几种方法可以做到这一点。您可以将列AVAILABLE
或OCCUPIED
添加到第一个表格
并使用where available = 'Y'
仅从此表中选择数据。在这种情况下,您还需要触发器
删除和更新第二个表上的location_id。
第二个选项 - 当merge
为空时,使用all_locations
或某些过程从item_inventory.location_id
检索数据时插入数据。
第三个选项 - Oracle 11g引入了compound triggers 这允许更好地处理变异表。在这种情况下,触发器看起来像这样:
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)看起来没问题。你可以尝试一下,检查它是否有效,也许这适合你。
最后的笔记 - 在您的选择中,您不需要明确,MINUS makes values distinct。
另外考虑订购数据,现在您的select
(和我的)可能会从ALL_LOCATIONS中随机排列。