简体   繁体   English

PL / SQL触发器变量问题

[英]PL/SQL Trigger Variable Problems

I am relatively new to PL/SQL and i am trying to create a trigger that will alert me after an UPDATE on a table Review. 我是PL / SQL的新手,我正在尝试创建一个触发器,该触发器将在对表Review进行UPDATE后提醒我。 When it is updated I want to ge the username(User table), score(Review Table), and product name (Product Table) and print them out: 更新后,我要生成用户名(用户表),得分(审阅表)和产品名称(产品表)并打印出来:

This is what I have so far: 这是我到目前为止的内容:

three tables: 三个表:

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

So Review can reference the other tables with forigen keys. 因此,Review可以使用原键来引用其他表。

    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;

The problem I am having is I cannot seem to figure out how to store the selected fields into the variables and output it correctly. 我遇到的问题是我似乎无法弄清楚如何将所选字段存储到变量中并正确输出。

DDL: 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)
);

The first problem I can see is that you're missing a colon when you refer to new.rid . 我看到的第一个问题是,当您引用new.rid时,您缺少了一个冒号。 The second is that you're accessing the review table inside a row-level trigger on that same table, which will give you a mutating table error at some point; 第二个原因是您正在访问同一表的行级触发器内的审阅表,这将在某个时候给您带来变异表错误。 but you don't need to as all the data from the inserted row is in the new pseudorow. 但是您不需要,因为插入行中的所有数据都在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;

The bigger problem is that the only person who could see the message is the user inserting tow row, which seems a bit pointless; 更大的问题是,唯一能看到此消息的人是用户插入拖车行,这似乎毫无意义。 and they would have to have output enabled in their session to see it. 并且他们必须在会话中启用输出才能看到它。

If you're trying to log that so someone else can see it then store it in a table or write it to a file. 如果您尝试记录该日志,以便其他人可以看到它,则将其存储在表中或将其写入文件。 As the review table can be queried anyway it seems a bit redundant though. 由于无论如何都可以查询检查表,尽管如此似乎有点多余。

Having all your table columns as strings is also not good - don't store numeric values (eg scores, and probably the ID fields) or dates as strings, use the correct data types. 将所有表列都作为字符串也是不好的-不要将数字值(例如分数,可能还有ID字段)或日期存储为字符串,请使用正确的数据类型。 It will save you a lot of pain later. 以后将为您节省很多痛苦。 You also don't seem to have any referential integrity (primary/foreign key) constraints - so you can review a product that doesn't exist, for instance, which will cause a no-data-found exception in the trigger. 您似乎也没有任何参照完整性(主键/外键)约束-例如,您可以查看不存在的产品,这将在触发器中导致找不到数据的异常。

It makes really no sense to use a trigger to notify themselves about changed rows. 使用触发器通知自己有关已更改的行的确没有任何意义。 If you insert new rows into the table, then you have all info about them. 如果在表中插入新行,则将获得有关它们的所有信息。 Why not something like the block below instead a trigger: 为什么不像下面的块这样的触发器呢?

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;
/

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

In order to inform another session about an event you should use dbms_alert (transactional) or dbms_pipe (non transactional) packages. 为了将事件通知另一个会话,您应该使用dbms_alert(事务性)或dbms_pipe(非事务性)包。 An example of dbms_alert: 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;
/

Run the following block in another session (new window, worksheet, sqlplus or whatever else). 在另一个会话中运行以下块(新窗口,工作表,sqlplus或其他)。 It will be blocked until the registered signal is arrived: 在收到注册信号之前,它将被阻止:

<<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;
/

Now in your session: 现在在您的会话中:

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

The another (observer) session will be finished with the output: 另一个(观察者)会话将以输出完成:
observer: new_review_alert for user=nobody,product=prod 1001: score=7

PS There was no RID in the Table REVIEW, so i'll just assume it was supposed to be PID. PS:表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;     

You just made a mistake on the INTO statement, you can just clump them together in one INTO. 您刚刚在INTO语句中犯了一个错误,您可以将它们合并到一个INTO中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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