Sql 使用基于函数的索引的延迟唯一约束?

Sql 使用基于函数的索引的延迟唯一约束?,sql,oracle,unique-constraint,Sql,Oracle,Unique Constraint,我认为这会有点深奥,但我想把它扔出去,以防有人尝试过类似的事情,或者如果有人已经尝试过并且发现这是不可能的 我们有一个表,它需要对某一组列进行唯一性约束,但它也有一个“软删除”指示符。已标记为“已删除”的记录不应包含在唯一性检查中 这很好,我可以用一个独特的基于函数的索引轻松解决这个问题。然而,更复杂的是,如果我们要在数据库中实现这个约束,它必须是一个延迟约束,因为Hibernate的工作方式。如果做不到,我们将不得不忽略约束,如果可能的话,我宁愿不这样做 例如: CREATE TABLE jk

我认为这会有点深奥,但我想把它扔出去,以防有人尝试过类似的事情,或者如果有人已经尝试过并且发现这是不可能的

我们有一个表,它需要对某一组列进行唯一性约束,但它也有一个“软删除”指示符。已标记为“已删除”的记录不应包含在唯一性检查中

这很好,我可以用一个独特的基于函数的索引轻松解决这个问题。然而,更复杂的是,如果我们要在数据库中实现这个约束,它必须是一个延迟约束,因为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\u TEST\u deleted
,但这会在应用程序中带来太多的复杂性)


这在Oracle 11.2.0.1.0上。

这在apex.Oracle.com后面的11.2.0.2中起作用。它应该在11.2.0.1中工作(可能在11.1中工作,但在10g中不工作,因为虚拟列是11g增强)

归功于

您必须为插入指定列列表,因为不应指定派生列(虚拟列)。我很确定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');
杰夫

这里有一种实现需求的方法,这种方法也适用于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上进行了测试。

+1测试并通过:对于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;