PL / SQL触发器变量问题

时间:2016-11-04 21:34:54

标签: oracle plsql

我是PL / SQL的新手,我正在尝试创建一个触发器,它会在表上的UPDATE后提醒我。当它更新时,我想ge用户名(用户表),得分(评论表)和产品名称(产品表)并打印出来:

这是我到目前为止所做的:

三个表:

Review: score, userid,pid, rid
Users: userid,uname
Product: pid,pname

因此,Review可以使用forigen键引用其他表。

    create or replace trigger userNameTrigger


    after insert on review
    for each row

    declare


    x varchar(256);
    y varchar(256);
    z varchar(256);


    begin


    select uname into x , pname into y  , score into z
from review r , product p , users u
where r.pid = p.pid and r.userid = u.userid and r.rid =new.rid;




    dbms_output.put_line('user: '|| X||'entered a new review for Product: '|| Y || 'with a review score of: '|| Z);


    end;

我遇到的问题是我似乎无法弄清楚如何将所选字段存储到变量中并正确输出。

DDL:

    Create Table Review
    (
        score    varchar2(100)
      , userid   varchar2(100)   
      , pid      varchar2(100) 
      , rid      varchar2(100) 
    );

Create Table Users
(
    userid   varchar2(100)
  , uname    varchar2(100)
);

Create Table Product
(
    pid      varchar2(100)
  , pname    varchar2(100)
);

3 个答案:

答案 0 :(得分:3)

我可以看到的第一个问题是,当您引用new.rid时,您错过了冒号。第二个是你在同一个表的行级触发器中访问查看表,这会在某个时刻给你一个变异表错误;但是您不需要,因为插入行中的所有数据都在new伪轨中。

create or replace trigger userNameTrigger
after insert on review
for each row
declare
  l_uname users.uname%type;
  l_pname product.pname%type;
begin
  select u.uname into l_uname
  from users u
  where u.userid = :new.userid;

  select p.pname
  into l_pname
  from product
  where p.pid = :new.pid;

  dbms_output.put_line('user '|| l_uname
    || ' entered a new review for product ' || l_pname
    || ' with a review score of '|| :new.score);
end;

更大的问题是,唯一可以看到消息的人是用户插入拖车行,这似乎有点无意义;他们必须在会话中启用输出才能看到它。

如果您正在尝试记录,以便其他人可以看到它,则将其存储在表中或将其写入文件。因为无论如何都可以查询评论表,但这似乎有点多余。

将所有表列作为字符串也不好 - 不要将数值(例如分数,可能是ID字段)或日期存储为字符串,请使用正确的数据类型。它会在以后为你节省很多痛苦。您似乎也没有任何参照完整性(主/外键)约束 - 因此您可以查看不存在的产品,例如,这将导致触发器中出现无数据的异常。

答案 1 :(得分:1)

使用触发器通知自己有关已更改的行是没有意义的。如果您在表中插入新行,那么您将获得有关它们的所有信息。为什么不像下面的块而不是触发器:

create table reviews as select 0 as rid, 0 as userid, 0 as score, 0 as pid from dual where 1=0;
create table users as select 101 as userid, cast('nobody' as varchar2(100)) as uname from dual;
create table products as select 1001 as pid, cast('prod 1001' as varchar2(100)) as pname from dual;

<<my>>declare newreview reviews%rowtype; uname users.uname%type; pname products.pname%type; begin 
    insert into reviews values(1,101,10,1001) returning rid,userid,score,pid into newreview;
    select uname, pname into my.uname, my.pname
    from users u natural join products p
    where  u.userid = newreview.userid and p.pid = newreview.pid
    ;
    dbms_output.put_line('user: '||my.uname||' entered a new review for Product: '||my.pname||' with a review score of: '||newreview.score);
end;
/

输出:user: nobody entered a new review for Product: prod 1001 with a review score of: 10

为了通知另一个会话有关事件,您应该使用dbms_alert(transactional)或dbms_pipe(非事务性)包。 dbms_alert的一个例子:

create or replace trigger new_review_trig after insert on reviews for each row
begin
    dbms_alert.signal('new_review_alert', 'signal on last rid='||:new.rid); 
end;
/

在另一个会话中运行以下块(新窗口,工作表,sqlplus或其他任何内容)。它将被阻止,直到注册信号到达:

<<observer>>declare message varchar2(400); status integer; uname users.uname%type; pname products.pname%type; score reviews.score%type;
begin
    dbms_alert.register('new_review_alert');
    dbms_alert.waitone('new_review_alert', observer.message, observer.status); 
    if status != 0 then raise_application_error(-20001, 'observer: wait on new_review_alert error'); end if;
    select uname, pname, score into observer.uname, observer.pname, observer.score
    from reviews join users using(userid) join products using (pid)
    where rid = regexp_substr(observer.message, '\w+\s?rid=(\d+)', 1,1,null,1) 
    ;
    dbms_output.put_line('observer: new_review_alert for user='||observer.uname||',product='||observer.pname||': score='||observer.score);
end;
/

现在在您的会话中:

insert into reviews values(2, 101,7,1001);
commit; --no alerting before commit 

另一个(观察者)会话将以输出结束:
observer: new_review_alert for user=nobody,product=prod 1001: score=7

答案 2 :(得分:0)

P.S。表REVIEW中没有RID,所以我只假设它应该是PID。

create or replace trigger userNameTrigger
after insert on review
for each row
declare

    x varchar2(256);
    y varchar2(256);
    z varchar2(256);

BEGIN

    select  uname 
          , pname
          , score
    INTO    x
          , y
          , z
    from   review r 
        ,  product p 
        ,  users u
    where  r.pid = p.pid 
    and    r.userid = u.userid 
    and    r.PID = :new.pid;
    dbms_output.put_line('user: '|| X ||'entered a new review for Product: '|| Y || 'with a review score of: '|| Z);

end userNameTrigger;     

你刚刚在INTO声明中犯了一个错误,你可以在一个INTO中将它们聚集在一起。