在插入新数据集之前获取表中已有事件的行数?

时间:2013-03-01 03:56:53

标签: sql oracle insert

我有一个由三个值组成的表

  1. 参与者的ID
  2. 课程活动的ID
  3. 标记
  4. 对于每个courseevent,只允许15个人

    如何使用Oracle检查?

5 个答案:

答案 0 :(得分:3)

此问题最重要的方面是使其在多用户环境中工作。

Oracle仅允许READ COMMITTED和SERIALIZED隔离级别。没有幻像或脏读,也没有在未提交的会话中“窥视”的机制。 Find out more

这意味着这句话

select courseevent, count(*) 
from courseparticpants
group by courseevent;

将显示已提交的记录数。如果您继续插入记录,您仍然可以插入第16个预订,如果其他人在此期间承诺他们的工作。相反,当事实上有人要删除一行时,你可能会认为课程已经满了。

要控制此操作,您需要序列化对courseparticpants表的访问权限,以便一次只能有一个会话将记录插入其中。有多种方法可以做到这一点,但最安全的是:

lock table courseparticpants exclusive nowait;

如果你没有获得锁定,你知道另一个会话已经在进行。否则,您可以运行您的计数,插入新的预订并执行其他所需的操作,并确信您的规则不会被破坏。

重要的是不要为了过于锁定而冻结锁,原因显而易见:没有其他人可以在桌子上做他们的工作。稍微不那么突兀的机制是将相关记录锁定在父表中;我没有先提出这个问题,因为我不想对你的数据模型做出假设。

select whatever
from courseevents
where courseevent = :p1
for update nowait;

这将允许其他会话预订其他活动的参与者。 Find out more

这两种解决方案都需要编写程序单元(比如PL / SQL)来管理事务。

  

“是否有可能通过约束来解决这个问题?”

不,Oracle不允许在其CHECK约束中使用SQL。标准SQL具有ASSERTIONS的概念,但Oracle尚未实现它们。

一种可能的解决方案是在participantid内使courseevent计数,以便您可以强制执行检查约束

check ( participantid <= 15)

但是,您仍然需要执行所有锁定操作以获取当前参与者数量的准确数字,以便n+1正确无误。

答案 1 :(得分:0)

select count(*)
from blah, blah, blah

为您提供现有记录的数量。

答案 2 :(得分:0)

这显示超过15个活动的参与者。

SELECT participant, COUNT(DISTINCT courseevent) F
FROM Table
GROUP BY participant
HAVING COUNT(DISTINCT courseevent) > 15

答案 3 :(得分:0)

INSERT INTO MyTable(Col1, Col2, Col3)
    SELECT 'Val1', 'Val2', 'Val3'
      FROM DUAL
     WHERE (SELECT COUNT(*) FROM MyTable WHERE condition) < 15;

答案 4 :(得分:0)

常规表约束仅隔离各个行,但您的要求是一起考虑。 这是一个相当复杂的解决方案,它使用物化视图约束来实现需求。 您可以将此视为定义结果集中列的约束。

create table course_participants(
   course          varchar2(20) not null
  ,participant     varchar2(20) not null
  ,constraint course_participants_pk primary key(course, participant)
);

-- Need this for fast refreshable mview
create materialized view log 
    on course_participants 
       with rowid(course, participant) 
       including new values;

-- A materialized view with a count of participants per course
create materialized view course_parts_max_mv
refresh fast on commit
as
select course
      ,count(*) as participants
 from course_participants
group 
   by course;

-- This is where you perform the check. 
-- I've used 2 participants to make the example easier
alter materialized view course_parts_max_mv 
  add constraint too_many_participants check(participants <= 2);

上面的DDL创建了一个表和一个物化视图。物化视图将包含每个课程的一行以及参与者的nr。诀窍是,我们现在可以在物化视图上声明它,而不是在基表上声明约束。

-- One participant is ok!
insert into course_participants values('Oracle', 'Alfred');
commit;

-- Two participants are ok!
insert into course_participants values('Englis speling', 'Benjamin');
insert into course_participants values('Englis speling', 'Charles');
commit;

-- This will fail, because the count(*) for 'Economics' will return 3
insert into course_participants values('Economics', 'Alfred');
insert into course_participants values('Economics', 'Benjamin');
insert into course_participants values('Economics', 'Charles');
commit;
ORA-12008: error in materialized view refresh path
ORA-02290: check constraint (RNBN.TOO_MANY_PARTICIPANTS) violated

请注意,当您提交交易时会检查约束,因此在最后一个示例中,没有参与者会被注册。