首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >sql oracle -对来自不同表的2列的约束

sql oracle -对来自不同表的2列的约束
EN

Stack Overflow用户
提问于 2021-12-14 19:20:38
回答 3查看 74关注 0票数 0

我为航班设计了机票系统。我想增加一个约束,这样你可以插入的机票数量少于一架飞机的座位数。

假设我插入了一架有10个座位的飞机。我只能为那次航班插入10张机票。否则,应出现错误消息。

我试着在航班号上使用计数函数做一个触发器。

代码语言:javascript
复制
CREATE OR REPLACE TRIGGER trg_ticket_BRIU 
    BEFORE INSERT OR UPDATE ON Ticket 
    FOR EACH ROW 
DECLARE
    l_numberofseats flight.numberofseats%type;
BEGIN
    select numberofseats into l_numberofseats
    from flight
    where flightnumber=:new.flightnumber;

    IF :new.count(flightnumber) > l_numberofseats
    THEN
        raise_application_error(-2000, 'Not enough seats');
    END IF;
END;

但我知道这个错误

代码语言:javascript
复制
Trigger TRG_TICKET_BRIU compiled

LINE/COL  ERROR
--------- -------------------------------------------------------------
8/5       PLS-00049: bad bind variable 'NEW.COUNT'
Errors: check compiler log
EN

回答 3

Stack Overflow用户

发布于 2021-12-14 21:00:31

就我个人而言,我会添加一个AIRCRAFT和一个SEAT表:

代码语言:javascript
复制
CREATE TABLE aircraft (
  id           NUMBER
               GENERATED ALWAYS AS IDENTITY
               CONSTRAINT aircraft__id__pk PRIMARY KEY,
  tail_number  VARCHAR2(6)
               NOT NULL
               CONSTRAINT aircraft__tn__u UNIQUE
               CONSTRAINT aircraft__tn_chk CHECK(
                 REGEXP_LIKE(
                   tail_number,
                   '[A-Z]\d{1,5}|[A-Z]\d{1,4}[A-Z]|[A-Z]\d{1,3}[A-Z]{2}'
                 )
               ),
  manufacturer VARCHAR2(20)
               NOT NULL,
  model        VARCHAR2(20)
               NOT NULL,
  airline_id   CONSTRAINT aircraft__aid__fk REFERENCES airline(airline_id)
               NOT NULL
);

CREATE TABLE seat (
  id           NUMBER
               GENERATED ALWAYS AS IDENTITY
               CONSTRAINT seat__id__pk PRIMARY KEY,
  aircraft_id  CONSTRAINT seat__aid__fk REFERENCES aircraft(id)
               NOT NULL,
  seat_row     VARCHAR2(3)
               NOT NULL,
  seat_column  NUMBER
               NOT NULL,
  CONSTRAINT seat__aid_r_c__u UNIQUE (aircraft_id, seat_row, seat_column)
);

那么您的flight表将引用aircraft

代码语言:javascript
复制
CREATE TABLE flight (
  id           NUMBER
               GENERATED ALWAYS AS IDENTITY
               CONSTRAINT flight__id__pk PRIMARY KEY,
  aircraft_id  CONSTRAINT flight__aid__fk REFERENCES aircraft(id)
               NOT NULL
  -- ...
);

ticket会引用flightseat

代码语言:javascript
复制
CREATE TABLE ticket (
  id           NUMBER
               GENERATED ALWAYS AS IDENTITY
               CONSTRAINT ticket__id__pk PRIMARY KEY,
  flight_id    CONSTRAINT ticket__fid__fk REFERENCES flight(id)
               NOT NULL,
  seat_id      CONSTRAINT ticket__sid__fk REFERENCES seat(id)
               NOT NULL,
  -- ...
  CONSTRAINT ticket__fid_sid__u UNIQUE (flight_id, seat_id)
);

然后,您就永远不能销售一个不存在于seat上的aircraft,并且不需要计算最大票数并将其与座位进行比较(而且seat添加了诸如其在飞机上的位置之类的属性,这些属性可以显示在机票上)。

然后,您只需要确保引用的一致性,对于ticketflightseat都在同一个aircraft上;这可以通过触发器来完成:

代码语言:javascript
复制
CREATE TRIGGER ticket_check_seat_on_flight
  BEFORE INSERT OR UPDATE ON ticket
  FOR EACH ROW
DECLARE
  is_valid NUMBER(1);
BEGIN
  SELECT 1
  INTO   is_valid
  FROM   flight f
         INNER JOIN seat s
         ON (f.aircraft_id = s.aircraft_id)
  WHERE  f.id = :NEW.flight_id
  AND    s.id = :NEW.seat_id;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    RAISE_APPLICATION_ERROR(
      -20000,
      'Flight and seat are on different aircraft.'
    );
END;
/

db<>fiddle https://dbfiddle.uk/?rdbms=oracle_18&fiddle=659ec800cf3432343f0dc32082616be9

票数 1
EN

Stack Overflow用户

发布于 2021-12-14 21:20:20

您可以使用AFTER STATEMENT触发器:

代码语言:javascript
复制
CREATE TRIGGER ticket__check_number_of_seats
  AFTER INSERT OR UPDATE OR DELETE ON ticket
DECLARE
  is_invalid NUMBER(1,0);
BEGIN
  SELECT 1
  INTO   is_invalid
  FROM   flight f
         INNER JOIN (
           SELECT flight_id,
                  COUNT(*) AS tickets_sold
           FROM   ticket
           GROUP BY flight_id
         ) t
         ON f.id = t.flight_id
  WHERE  t.tickets_sold > f.number_of_seats;
  
  RAISE_APPLICATION_ERROR(
    -20000,
    'Too many tickets sold for flight.'
  );
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    NULL;
END;
/

通过使用复合触发器将每一行的flight_id值排序为集合,然后在语句之后只检查这些航班的机票数量,可以提高效率;但是,我将保留这个扩展作为OP的练习。

db<>fiddle https://dbfiddle.uk/?rdbms=oracle_18&fiddle=054f47db8dd219cc90873ba404c49d9d

票数 0
EN

Stack Overflow用户

发布于 2021-12-14 22:15:30

正如其他人所指出的,没有:new.count列。这是因为:new (and :old)创建一个伪行,其中包含与表定义完全相同的列。此外,您将从Mutating获得flight_number中需要计算的一个tickets异常。但是,因为这是导致he触发器触发的表,所以不能引用它。那么要做什么:创建一个复合触发器和一个支持类型(嵌套表)。在其中,使用after row部分来捕获已处理的flight_numbers。然后,在after statement部分,您可以选择每次航班的机票数量。如果计数>0,则引发异常。(见演示 )

代码语言:javascript
复制
create type flight_tickets_ntt
         is table of integer;

create or replace trigger trg_ticket_ciu
   for update or insert on tickets
       compound trigger
       
    l_flights  flight_tickets_ntt := flight_tickets_ntt(); 
    
    after each row is 
    begin 
        if :new.flight_number not member of l_flights then 
            l_flights.extend ;
            l_flights(l_flights.count) := :new.flight_number; 
        end if; 
    end after each row; 
    
    after statement is
        l_flight_cnt flight.flight_number%type;
    begin 
         select count(*)
           into l_flight_cnt
           from flight f 
          where f.number_of_seats < 
                ( select count(*)
                    from tickets t 
                   where t.flight_number in 
                         ( select * 
                             from table (l_flights)
                         ) 
                );  
       
          if l_flight_cnt > 0 then 
             raise_application_error(-20000, 'Not enough seats');
          end if; 
           
    end after statement; 
end trg_ticket_ciu; 

还有一个您需要处理的问题:如果更新更改了航班号,或者可能更改了航班的数据(缺少的列),会发生什么情况。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70354491

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档