交叉 table 数据验证
Cross-table data validation
需要你的帮助
我有 2 个关联实体:图书馆 reader(订阅者)和图书发行。 Reader 由于违反图书馆规则,可能会被锁定一段时间:
我需要添加检查,以便 reader,他的锁定期尚未结束,无法在图书馆借书(换句话说,Issue.Taken > Subscriber.Lock_Date+Subscriber.Lock_Period)
请帮忙,我该怎么做?
这真的应该在业务逻辑中处理,而不是在 table 级别。
但是,您可以使用物化视图来实现:
CREATE TABLE subscriber (
id INT PRIMARY KEY,
lock_date DATE,
lock_period INTERVAL DAY(5) TO SECOND
);
CREATE TABLE issue (
id INT PRIMARY KEY,
subscr_id INT NOT NULL REFERENCES subscriber( id ),
book_id INT,
taken DATE,
returned DATE
);
CREATE MATERIALIZED VIEW LOG ON subscriber
WITH SEQUENCE, ROWID( id, lock_date, lock_period )
INCLUDING NEW VALUES;
CREATE MATERIALIZED VIEW LOG ON issue
WITH SEQUENCE, ROWID( subscr_id, taken )
INCLUDING NEW VALUES;
CREATE MATERIALIZED VIEW subscriber_issue_MV
BUILD IMMEDIATE
REFRESH FAST ON COMMIT
AS SELECT s.id,
s.lock_date,
s.lock_period,
i.taken
FROM subscriber s
INNER JOIN
issue i
ON ( i.subscr_id = s.id );
ALTER TABLE subscriber_issue_MV ADD CONSTRAINT subscriber_issue__mv__chk
CHECK ( lock_date IS NULL
OR lock_period IS NULL
OR NOT taken BETWEEN lock_date AND lock_date + lock_period );
我同意 MTO 的观点,即此类验证应由应用程序代码处理(通过存储过程)。但是,如果您坚持通过数据库执行此验证,那么以下触发器将会有所帮助。同样,我不推荐此解决方案,最好的方法是使用应用程序逻辑来处理它。
CREATE OR REPLACE TRIGGER trg_val_lock_dt
BEFORE INSERT ON issue
FOR EACH ROW
DECLARE
v_is_valid CHAR(1);
BEGIN
v_is_valid := 'Y';
SELECT 'N' INTO v_is_valid
FROM subscriber s
WHERE :NEW.subscr_id = s.subscr_id
AND :NEW.taken BETWEEN s.lock_date AND (s.lock_date + lock_period);
RAISE_APPLICATION_ERROR(-20001,'The subscriber is locked');
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
END;
上述触发器将在 issue table 中的每个插入之前触发。它将检查拍摄日期是否介于锁定日期和锁定日期 + 锁定期(这将是锁定结束日期)之间。如果找到这样的记录,那么它将抛出以下错误并且不会插入该行。
ORA-20001: The subscriber is locked
ORA-06512: at "RETAIL_1.TRG_VAL_LOCK_DT", line 12
如果不满足条件,则将引发未找到数据异常,触发器将不执行任何操作并插入行。
需要你的帮助
我有 2 个关联实体:图书馆 reader(订阅者)和图书发行。 Reader 由于违反图书馆规则,可能会被锁定一段时间:
我需要添加检查,以便 reader,他的锁定期尚未结束,无法在图书馆借书(换句话说,Issue.Taken > Subscriber.Lock_Date+Subscriber.Lock_Period)
请帮忙,我该怎么做?
这真的应该在业务逻辑中处理,而不是在 table 级别。
但是,您可以使用物化视图来实现:
CREATE TABLE subscriber (
id INT PRIMARY KEY,
lock_date DATE,
lock_period INTERVAL DAY(5) TO SECOND
);
CREATE TABLE issue (
id INT PRIMARY KEY,
subscr_id INT NOT NULL REFERENCES subscriber( id ),
book_id INT,
taken DATE,
returned DATE
);
CREATE MATERIALIZED VIEW LOG ON subscriber
WITH SEQUENCE, ROWID( id, lock_date, lock_period )
INCLUDING NEW VALUES;
CREATE MATERIALIZED VIEW LOG ON issue
WITH SEQUENCE, ROWID( subscr_id, taken )
INCLUDING NEW VALUES;
CREATE MATERIALIZED VIEW subscriber_issue_MV
BUILD IMMEDIATE
REFRESH FAST ON COMMIT
AS SELECT s.id,
s.lock_date,
s.lock_period,
i.taken
FROM subscriber s
INNER JOIN
issue i
ON ( i.subscr_id = s.id );
ALTER TABLE subscriber_issue_MV ADD CONSTRAINT subscriber_issue__mv__chk
CHECK ( lock_date IS NULL
OR lock_period IS NULL
OR NOT taken BETWEEN lock_date AND lock_date + lock_period );
我同意 MTO 的观点,即此类验证应由应用程序代码处理(通过存储过程)。但是,如果您坚持通过数据库执行此验证,那么以下触发器将会有所帮助。同样,我不推荐此解决方案,最好的方法是使用应用程序逻辑来处理它。
CREATE OR REPLACE TRIGGER trg_val_lock_dt
BEFORE INSERT ON issue
FOR EACH ROW
DECLARE
v_is_valid CHAR(1);
BEGIN
v_is_valid := 'Y';
SELECT 'N' INTO v_is_valid
FROM subscriber s
WHERE :NEW.subscr_id = s.subscr_id
AND :NEW.taken BETWEEN s.lock_date AND (s.lock_date + lock_period);
RAISE_APPLICATION_ERROR(-20001,'The subscriber is locked');
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
END;
上述触发器将在 issue table 中的每个插入之前触发。它将检查拍摄日期是否介于锁定日期和锁定日期 + 锁定期(这将是锁定结束日期)之间。如果找到这样的记录,那么它将抛出以下错误并且不会插入该行。
ORA-20001: The subscriber is locked
ORA-06512: at "RETAIL_1.TRG_VAL_LOCK_DT", line 12
如果不满足条件,则将引发未找到数据异常,触发器将不执行任何操作并插入行。