简体   繁体   中英

How do I create a conditional unique constraint across multiple tables?

I'm working on a web application using Hibernate 4 and Oracle 11g.

I have the following tables that I'm working with in this scenario. Tables have been changed and simplified to protect the innocent.

entry
ID  |   name    |
1   |   thing1  |
2   |   thing2  |

entry_number
ID  |   value   |   entry_id|   type_id |
1   |   11111   |   1       |   1       |
2   |   22222   |   1       |   2       |
3   |   33333   |   1       |   2       |
4   |   aaaaa   |   2       |   1       |
5   |   bbbbb   |   2       |   2       |
6   |   ccccc   |   2       |   2       |

type
ID  |   name    |
1   |   unique  |
2   |   regular |
3   |   etc.    |
...

The idea is that I want to conditionally restrict insertions of entry_number so that there can only be one number of type "unique" assigned to any given entry. Unfortunately, many of the straightforward constraint approaches don't work for this scenario. After some research, I've found the following solution works:

create unique index unique_entry_number on entry_number(CASE WHEN TYPE_ID = 1 THEN entry_id ELSE null END);

The only thing I don't like about this is that I am referencing the id for "type_id", which I don't believe I can necessarily depend on to be consistent. And Oracle won't let me use a subquery inside of the unique index to join on "type.name" which I can depend on to be consistent.

Is there a different approach I should use that I'm not aware of or are there any suggestions on how I might mitigate this problem? Preferably one that is as un-intrusive as possible in respect to code changes or data model changes? Or is this just a reality I will have to learn to deal with?

It turns out that you can have a check constraint on a materialized view; so, you should be able to write something like this:

CREATE MATERIALIZED VIEW LOG
    ON entry_number
    WITH ROWID
;
CREATE MATERIALIZED VIEW LOG
    ON type
    WITH ROWID
;
CREATE MATERIALIZED VIEW entry_number_counter
    REFRESH FAST
    ON COMMIT
    AS SELECT en.entry_id, COUNT(1) AS row_count
           FROM entry_number en
           JOIN type ON entry_number.type_id = type.id
           WHERE type.name = 'unique'
           GROUP BY en.entry_id, type.name
;
ALTER TABLE entry_number_counter
    ADD CONSTRAINT entry_id_conditionally_unique
       CHECK (row_count = 1)
;

(Disclaimer: not tested. This is adapted from an example in Tom Kyte's "The Trouble With Triggers" . If you decide to go this route, you'll want to read through the documentation first and understand all of it; in particular, I'm not very clear on the "materialized view log" part, and it may need some adjustments.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM