我认为这会有点深奥但是想把它扔出去,万一有人试过这样的事情,或者如果有人已经尝试过并发现它是不可能的。
我们有一个表需要对某组列进行唯一性约束,但它还有一个“软删除”指示符。已标记为“已删除”的记录不应包含在唯一性检查中。
这很好,我可以使用基于函数的独特索引轻松解决这个问题。然而,使问题复杂化的是,如果我们要在数据库中实现这个约束,它必须是延迟约束,因为Hibernate的工作方式。如果无法完成,我们将不得不省略约束,如果可能的话,我宁愿不这样做。
例如:
CREATE TABLE jkemp_test
( id NUMBER NOT NULL
, deleted_ind CHAR(1) DEFAULT 'N' NOT NULL);
CREATE UNIQUE INDEX jkemp_test_funique
ON jkemp_test
(CASE WHEN deleted_ind = 'N' THEN id END);
-- make this use the function-based index, maybe?
ALTER TABLE jkemp_test
ADD CONSTRAINT jkemp_test_unique
UNIQUE (id)
DEFERRABLE INITIALLY DEFERRED;
INSERT INTO jkemp_test VALUES (1,'N');
INSERT INTO jkemp_test VALUES (2,'N');
COMMIT;
UPDATE jkemp_test SET deleted_ind='Y' WHERE id=1;
COMMIT;
-- depending on whether the constraint is deferred or not, either
-- the insert or the commit will fail "unique constraint violated"
INSERT INTO jkemp_test VALUES (1,'N');
COMMIT;
双赢场景将是最后一次提交成功,同时仍然允许延迟约束。 (我知道唯一索引的存在意味着当前没有推迟约束。)
目前我们唯一的选择是使用应用程序实现约束,这不会那么可靠。此外,我们不希望过多地更改数据模型(例如,我们可以将删除的行移动到不同的表格,例如JKEMP_TEST_DELETED
,但这会在应用程序中引入太多复杂化。)
这是在Oracle 11.2.0.1.0上。
答案 0 :(得分:5)
这适用于apex.oracle.com后面的11.2.0.2。它应该在11.2.0.1中工作(可能在11.1中,但不是在10g中,因为虚拟colums是11g增强)
归功于Lucas Jellma
CREATE TABLE jkemp_test
( id NUMBER NOT NULL
, deleted_ind CHAR(1) DEFAULT 'N' NOT NULL);
alter table jkemp_test
ADD (active_id AS (CASE WHEN deleted_ind = 'N' THEN id END))
/
ALTER TABLE jkemp_test
ADD CONSTRAINT jkemp_test_unique
UNIQUE (active_id)
DEFERRABLE INITIALLY DEFERRED;
您必须指定插入的列列表,因为不应指定派生列(虚拟列)。我非常确定hibernate可以使用不接触的列。
INSERT INTO jkemp_test (id, deleted_ind) VALUES (1,'N');
INSERT INTO jkemp_test (id, deleted_ind) VALUES (2,'N');
COMMIT;
UPDATE jkemp_test SET deleted_ind='Y' WHERE id=1;
COMMIT;
INSERT INTO jkemp_test (id, deleted_ind) VALUES (1,'N');
答案 1 :(得分:2)
杰夫,
以下是实现11g之前版本的要求的一种方法。
DROP MATERIALIZED VIEW MV_JKEMP ;
DROP MATERIALIZED VIEW LOG ON jkemp_test ;
DROP TABLE JKEMP_TEST ;
CREATE TABLE jkemp_test
( id NUMBER NOT NULL
, deleted_ind CHAR(1) DEFAULT 'N' NOT NULL);
CREATE MATERIALIZED VIEW LOG ON jkemp_test WITH ROWID ;
CREATE MATERIALIZED VIEW MV_JKEMP
REFRESH FAST ON COMMIT
AS
SELECT JT1.ROWID r1, JT2.ROWID r2
FROM JKEMP_TEST JT1, JKEMP_TEST JT2
WHERE JT1.ID = JT2.ID
AND JT1.DELETED_IND = JT2.DELETED_IND
AND JT1.ROWID != JT2.ROWID
AND JT1.DELETED_IND = 'N' ;
ALTER TABLE MV_JKEMP ADD CONSTRAINT MV_CHECK CHECK (R1 IS NULL OR R2 IS NULL)
INSERT INTO jkemp_test VALUES (1,'N');
INSERT INTO jkemp_test VALUES (2,'N');
COMMIT;
UPDATE jkemp_test SET deleted_ind='Y' WHERE id=1 AND deleted_ind = 'N';
COMMIT;
SELECT * FROM JKEMP_TEST ;
SELECT * FROM MV_JKEMP;
INSERT INTO JKEMP_TEST VALUES (1,'N');
COMMIT;
SELECT * FROM JKEMP_TEST ;
SELECT * FROM MV_JKEMP;
-- The following will succeed on the INSERT but fail on COMMIT
INSERT INTO JKEMP_TEST VALUES (1,'N');
COMMIT;
-- The following will succeed
INSERT INTO JKEMP_TEST VALUES (3,'N');
COMMIT;
SELECT * FROM JKEMP_TEST ;
SELECT * FROM MV_JKEMP;
-- The following will succeed
UPDATE JKEMP_TEST SET DELETED_IND='Y' WHERE ID=1 AND DELETED_IND = 'N';
COMMIT;
SELECT * FROM JKEMP_TEST ;
SELECT * FROM MV_JKEMP;
DELETE FROM JKEMP_TEST ;
COMMIT;
上述内容在10.2.0.1上进行了测试。