How to use oracle check constraints to limit number of registration?

后端 未结 3 1108
礼貌的吻别
礼貌的吻别 2021-01-28 14:57

I\'ve a user table with unique user_id. User can register using there id. Now I want to limit the max. registration per user using CHECK constraints. so I use this:



        
3条回答
  •  傲寒
    傲寒 (楼主)
    2021-01-28 15:25

    You can't use check constraints on subqueries for this. You can, however, use triggers. A simple use of row-level trigger on a table selecting from the same table and raising an exception on count > 4 would result in ORA-4901, so you'd need composite triggers for this. But that is a bit of overkill already.

    So, I suggest that you might use a separate table with aggregated counts of user_ids updated via trigger and check-constrain that count. Source code:

    create table user_aggr
    (
        user_id             integer not null primary key,
        user_count          integer not null check (user_count <= 4)
    );
    

    You'll need to create a foreign key reference to the user_aggr table on your user table (named user_registration in my example):

    create table user_registration
    (
        registration_id             integer primary key,
        user_id                     integer references user_aggr
    );
    

    ... or just creating the FK constraint ...

    alter table user_registration
    add constraint FK_user_registration (user_id)
    references user_aggr;
    

    ... plus the necessary index over the FK ...

    create index user_registration_i0
    on user_registration (user_id);
    

    Then you'll need the calculation trigger:

    create or replace trigger auto_user_counter
        after insert or delete or update of user_id
        on user_registration
        for each row
    begin
        if updating or deleting then
            merge into user_aggr T
            using dual
            on ( T.user_id = :old.user_id )
            when matched then
                update set T.user_count = T.user_count - 1
                delete where T.user_count = 0
            ;
        end if;
    
        if inserting or updating then
            merge into user_aggr T
            using dual
            on ( T.user_id = :new.user_id )
            when matched then
                update set T.user_count = T.user_count + 1
            when not matched then
                insert (user_id, user_count) values (:new.user_id, 1)
            ;
        end if;
    end;
    /
    

    And now you're all set and ready to rumble. Examples ...

    insert into user_registration (registration_id, user_id) values(1, 1); -- OK
    insert into user_registration (registration_id, user_id) values(2, 1); -- OK
    insert into user_registration (registration_id, user_id) values(3, 1); -- OK
    insert into user_registration (registration_id, user_id) values(4, 1); -- OK
    insert into user_registration (registration_id, user_id) values(5, 1); -- ERROR
    
    insert into user_registration (registration_id, user_id) values(11, 2); -- OK
    insert into user_registration (registration_id, user_id) values(12, 2); -- OK
    insert into user_registration (registration_id, user_id) values(13, 2); -- OK
    insert into user_registration (registration_id, user_id) values(14, 2); -- OK
    insert into user_registration (registration_id, user_id) values(15, 2); -- ERROR
    
    delete from user_registration where user_id = 2 and rownum <= 1; -- OK; user_id 1: 4 rows; user_id 2: 3 rows
    update user_registration set user_id = 2 where user_id = 1 and rownum <= 1; -- OK; user_id 1: 3 rows; user_id 2: 4 rows
    update user_registration set user_id = 2 where user_id = 1 and rownum <= 1; -- ERROR on user_id 4
    

提交回复
热议问题