我们有两张桌子:
车辆:
分配:
使用最新分配更新Vehicle表中每一行的最有效(最简单)方法是什么?在SQL Server中,我将使用UPDATE FROM并使用最新的分配连接每个Vehicle。 Oracle没有UPDATE FROM。你是如何在Oracle中做到的?
**编辑**
我要求更新的最佳SQL查询。我将使用触发器来更新主表中的数据。我知道如何编写触发器。我要问的是如何编写SQL查询来更新Vehicle表。示例会很好。谢谢。
答案 0 :(得分:2)
当前设置要求您在ALLOCATIONS表上使用触发器来维护糟糕的决策选择。也就是说,使用:
UPDATE VEHICLE
SET (LastAllocationUserName, LastAllocationDate, LastAllocationId) =
(SELECT a.username,
a.date,
a.id
FROM ALLOCATIONS a
JOIN (SELECT b.vehicleid,
MAX(b.date) AS max_date
FROM ALLOCATIONS b
GROUP BY b.vehicleid) x ON x.vehicleid = a.vehicleid
AND x.max_date = a.date
WHERE a.vehicleid = VEHICLE.id)
通过从VEHICLE表中删除违规列并使用视图提供最新的分配信息,可以更好地满足这一要求。
答案 1 :(得分:2)
从设计的角度来看,我更倾向于在Vehicle表上主动维护三个字段,并将“Allocations”填充为历史表(可能通过触发器)。更容易将父表上的更新推送到子项上的插入,而不是相反。
答案 2 :(得分:2)
正如大多数人所说:由于您的数据模型,您遇到了一个大问题。为这个模型编写的大多数代码将比它需要的困难得多。我已经通过向上和向下的方式以及一些评论中说过,但它不能说得足够。
如果您继续沿着自己的方向前进,那么下面的代码将演示需要完成的工作。希望它会吓到你: - )
样本表:
SQL> create table vehicles (id,registrationnumber,lastallocationusername,lastallocationdate,lastallocationid)
2 as
3 select 1, 1, 'Me', sysdate-1, 2 from dual union all
4 select 2, 2, 'Me', sysdate, 3 from dual
5 /
Table created.
SQL> create table allocations (id,vehicleid,username,mydate)
2 as
3 select 1, 1, 'Me', sysdate-2 from dual union all
4 select 2, 1, 'Me', sysdate-1 from dual union all
5 select 3, 2, 'Me', sysdate-1 from dual
6 /
Table created.
触发器必须查看自己的表以确定最后的分配。 Oracle通过引发变异表错误来防止此类脏读。为了避免这种情况,我创建了一个SQL类型和一个包:
SQL> create type t_vehicle_ids is table of number;
2 /
Type created.
SQL> create package allocations_mutating_table
2 as
3 procedure reset_vehicleids;
4 procedure store_vehicleid (p_vehicle_id in vehicles.id%type);
5 procedure adjust_vehicle_last_allocation;
6 end allocations_mutating_table;
7 /
Package created.
SQL> create package body allocations_mutating_table
2 as
3 g_vehicle_ids t_vehicle_ids := t_vehicle_ids()
4 ;
5 procedure reset_vehicleids
6 is
7 begin
8 g_vehicle_ids.delete;
9 end reset_vehicleids
10 ;
11 procedure store_vehicleid (p_vehicle_id in vehicles.id%type)
12 is
13 begin
14 g_vehicle_ids.extend;
15 g_vehicle_ids(g_vehicle_ids.count) := p_vehicle_id;
16 end store_vehicleid
17 ;
18 procedure adjust_vehicle_last_allocation
19 is
20 begin
21 update vehicles v
22 set ( v.lastallocationusername
23 , v.lastallocationdate
24 , v.lastallocationid
25 ) =
26 ( select max(a.username) keep (dense_rank last order by a.mydate)
27 , max(a.mydate)
28 , max(a.id) keep (dense_rank last order by a.mydate)
29 from allocations a
30 where a.vehicleid = v.id
31 )
32 where v.id in (select column_value from table(cast(g_vehicle_ids as t_vehicle_ids)))
33 ;
34 end adjust_vehicle_last_allocation
35 ;
36 end allocations_mutating_table;
37 /
Package body created.
然后有3个数据库触发器将更新代码从行级别移动到语句级别,从而避免了变异表错误:
SQL> create trigger allocations_bsiud
2 before insert or update or delete on allocations
3 begin
4 allocations_mutating_table.reset_vehicleids;
5 end;
6 /
Trigger created.
SQL> create trigger allocations_ariud
2 after insert or update or delete on allocations
3 for each row
4 begin
5 allocations_mutating_table.store_vehicleid(nvl(:new.vehicleid,:old.vehicleid));
6 end;
7 /
Trigger created.
SQL> create trigger allocations_asiud
2 after insert or update or delete on allocations
3 begin
4 allocations_mutating_table.adjust_vehicle_last_allocation;
5 end;
6 /
Trigger created.
进行一些测试以验证它是否在单个用户环境中工作:
SQL> select * from vehicles
2 /
ID REGISTRATIONNUMBER LA LASTALLOCATIONDATE LASTALLOCATIONID
---------- ------------------ -- ------------------- ----------------
1 1 Me 13-05-2010 14:03:43 2
2 2 Me 14-05-2010 14:03:43 3
2 rows selected.
SQL> insert into allocations values (4, 1, 'Me', sysdate)
2 /
1 row created.
SQL> select * from vehicles
2 /
ID REGISTRATIONNUMBER LA LASTALLOCATIONDATE LASTALLOCATIONID
---------- ------------------ -- ------------------- ----------------
1 1 Me 14-05-2010 14:03:43 4
2 2 Me 14-05-2010 14:03:43 3
2 rows selected.
SQL> update allocations
2 set mydate = mydate - 2
3 where id = 4
4 /
1 row updated.
SQL> select * from vehicles
2 /
ID REGISTRATIONNUMBER LA LASTALLOCATIONDATE LASTALLOCATIONID
---------- ------------------ -- ------------------- ----------------
1 1 Me 13-05-2010 14:03:43 2
2 2 Me 14-05-2010 14:03:43 3
2 rows selected.
SQL> delete allocations
2 where id in (2,4)
3 /
2 rows deleted.
SQL> select * from vehicles
2 /
ID REGISTRATIONNUMBER LA LASTALLOCATIONDATE LASTALLOCATIONID
---------- ------------------ -- ------------------- ----------------
1 1 Me 12-05-2010 14:03:43 1
2 2 Me 14-05-2010 14:03:43 3
2 rows selected.
现在,您所要做的就是添加一些序列化,使其在多用户环境中100%运行。但希望这个例子足够吓人。
此致 罗布。
答案 3 :(得分:0)
使用Oracle中的另一个表“更新”的最简单方法是使用MERGE。
MERGE INTO vehicle v
USING (
-- subquery to get info you need
) s ON (v.id = s.vehicleId)
WHEN MATCHED THEN UPDATE SET
username = s.username
...
答案 4 :(得分:0)
您是否正在寻找触发器内的更新?
CREATE TRIGGER ALLOCATION_I
AFTER INSERT ON ALLOCATION
REFERENCING NEW AS NEW
FOR EACH ROW
Begin
UPDATE Vehicle
set LastAllocationUserName = :NEW.Username
,LastAllocationDate = :NEW.date
,LastAllocationId = :NEW.id
WHERE Id = :NEW.VehicleId;
END;
答案 5 :(得分:-1)
UPDATE VEHICLE V
SET (LastAllocationId, LastAllocationDate, LastAllocationUserName) =
(SELECT a.id
,a.date
,a.username
FROM ALLOCATIONS a
where a.VehicleId = V.id
and a.date = ( select max(Last_a.date) from ALLOCATIONS Last_a
where Last_a.VehicleId = V.id )
)
你是对的。带历史记录表的视图很慢。没有快速的“加入到最后记录”这样的事情。触发器是最佳解决方案。
如果可以,请使用PL首次填充。它更容易理解和保持。
DECLARE
Last_date DATE;
Last_User Varchar2(100);
Last_ID number;
Begin
FOR V IN ( Select * from VEHICLE )
LOOP
select max(date) into Last_date
from ALLOCATIONS Last_a
where Last_a.VehicleId = V.id;
IF Last_date is NULL then
Last_User := NULL;
Last_ID := NULL;
else
select Id,UserName into Last_id, Last_user
from ALLOCATIONS Last_a
where Last_a.VehicleId = V.id
and Last_a.date = Last_date;
END IF;
UPDATE Vehicle
set LastAllocationUserName = Last_User
,LastAllocationDate = Last_date
,LastAllocationId Last_id
Where id = V.id;
END LOOP;
End;
警告:写在这里,未经测试。