根据同一张表中的其他数据验证插入的数据(Oracle)
Posted
技术标签:
【中文标题】根据同一张表中的其他数据验证插入的数据(Oracle)【英文标题】:Validate data inserted based on other data in the same table (Oracle) 【发布时间】:2020-01-04 13:45:20 【问题描述】:我在 SQL Oracle 中做一个项目并发现了一个问题,因为我对 SQL 很陌生。
我有一个管理预订房间的系统,在一个名为 bookings 的表中。一些属性是:
room_id 到达日期 nights_nr booking_id(即 PK)。我有一个 UNIQUE(room_id,arrival_date) 约束。
为了有一个连贯的数据库,我需要检查每个插入或更新的行,是否已经预订了该房间的任何预定预订天数。这是基于表值的约束,我需要一个选择,因此我无法在 CHECK 中执行此操作(或者至少我还没有想出另一种可能性)。
我正在尝试使用触发器来执行此操作,但这是我第一次接触触发器。
我的想法是在 when 中有一个选择,并验证对于另一个 booking_id 和我目前打算更新的相同 room_id,我打算预订的天数范围是否与预订的范围相交。
由于缺乏知识,我写了这样的东西:
CREATE OR REPLACE TRIGGER validate_free_room
BEFORE INSERT OR UPDATE OF arrival_date, night_nr ON bookings
FOR each row
DECLARE a char(8)
DECLARE b char(3)
SET a=booking_id
SET b=room_id
WHEN EXISTS (
SELECT booking_id
FROM bookings r
WHERE
(r.booking_id!=a
AND
((arrival_date BETWEEN r.arrival_date AND r.arrival_date + nights_nr)
OR
(arrival_date + nights_nr BETWEEN r.arrival_date AND r.arrival_date + r.nights_nr)
)
AND
b=r.room_id
)
)
BEGIN
RAISE_APPLICATION_ERROR (-20107,'Room already booked')
end;
我刚刚发现我无法在使用“每一行”修改的表中执行选择。
您对我如何以正确的方式做到这一点有任何想法吗? (我知道前面的台词完全是一场灾难)。
我正在使用 Oracle Application Express,它给了我以下错误建议:
ORA-24344: success with compilation error
ORA-06512: at "SYS.WWV_DBMS_SQL_APEX_190200", line 592
ORA-06512: at "SYS.DBMS_SYS_SQL", line 1658
ORA-06512: at "SYS.WWV_DBMS_SQL_APEX_190200", line 578
ORA-06512: at "APEX_190200.WWV_FLOW_DYNAMIC_EXEC", line 2057
3. for each row
4. declare a char(8)
5. declare b char(3)
6. set a=id_rezerva
7. set b=id_camera
和
Error computing plan for statement.
ORA-00900: invalid SQL statement
【问题讨论】:
。 .注意:您的重叠逻辑不正确。但是您遇到的更重要的问题是变异数据触发错误。您应该清楚自己遇到的错误。 这很难解决。我可能会建议您从一个更简单的问题开始,或者在应用程序中进行检查(使用存储过程),或者使用不同的数据库。这是一个讨论:stevenfeuersteinonplsql.blogspot.com/2016/12/…. 您需要另一个条件来查找完全在您建议的日期之间的现有预订。也可能最好在您的日期中使用时间组件,并让所有到达时间开始于 1300 小时,出发时间发生在 1000 小时。如果您只是使用日期/挂钩所有时间到相同的值,那么 BETWEEN 将防止客人 2 在客人 1 腾出房间的同一天入住房间(包括介于两者之间),但每家酒店都希望支持同一天的入住率变化一个房间 如果您真的需要使用触发器,请使用语句触发器,而不是行触发器。有关示例,请参见 here。 如果 OP 不熟悉触发器,我强烈建议他阅读以下内容:blogs.oracle.com/oraclemagazine/the-trouble-with-triggers 和 asktom.oracle.com/pls/apex/… 和 docs.oracle.com/cd/B28359_01/appdev.111/b28370/… 【参考方案1】:还有另一种不涉及触发器的方法:
创建一个materialized view
,它在自身上连接表并且仅在重叠时才包含行。
此物化视图应在提交时刷新。
它有一个约束1=0
,每次出现一行时都会失败。
所以在每次提交时,如果有重叠,提交将失败,物化视图将始终为空。
有一些 DBA 类型的事情要做,所以这会执行得很好,例如收集统计信息在实体化视图日志为空时,然后锁定这些统计信息。
SQL> create table bookings(
2 booking_id integer primary key,
3 room_id integer not null,
4 arrival_date date not null check (arrival_date = trunc(arrival_date)),
5 nights_nr integer not null,
6 UNIQUE(room_id, arrival_date)
7 );
Table BOOKINGS created.
SQL> create materialized view log on bookings with rowid including new values;
Materialized view log BOOKINGS created.
SQL> create materialized view bookings_conflicts
2 refresh fast on commit as
3 select a.rowid arid, b.rowid brid
4 from bookings a, bookings b
5 where a.room_id = b.room_id
6 and a.arrival_date < b.arrival_date
7 and a.arrival_date + a.nights_nr > b.arrival_date;
Materialized view BOOKINGS_CONFLICTS created.
SQL> alter materialized view bookings_conflicts add constraint no_overlaps check(1=0) deferrable;
Materialized view BOOKINGS_CONFLICTS altered.
SQL> insert into bookings
2 select 1, 1, date '2020-01-01', 5 from dual union all
3 select 2, 1, date '2020-01-05', 1 from dual union all
4 select 3, 1, date '2020-01-06', 1 from dual;
3 rows inserted.
SQL> commit;
Error starting at line : 24 in command -
commit
Error report -
ORA-12008: error in materialized view or zonemap refresh path
ORA-02290: check constraint (STEW.NO_OVERLAPS) violated
....
【讨论】:
非常感谢!很有用!以上是关于根据同一张表中的其他数据验证插入的数据(Oracle)的主要内容,如果未能解决你的问题,请参考以下文章