【问题标题】:Change status automatically in oracle table在 oracle 表中自动更改状态
【发布时间】:2021-12-07 00:13:42
【问题描述】:

我有两张桌子

user (user_id, username, status )

状态 --> (1 - 主动, 0 - 被动)

user_work (user_work_id, user_id, status)

状态 --> (1 - 主动, 0 - 被动)

如果在 user_work 表中某些用户没有活动状态,如何在 user 表中自动设置状态 = 0。如果在 user 表中为某些处于 user_work 活动状态的用户设置状态 = 1

是否可以使用触发器或使用其他解决方案,例如调用某些程序?

【问题讨论】:

  • 如果用户表中不存在用户,我猜你不能在 user_work 表中插入或更新行。我说的对吗?

标签: sql oracle plsql


【解决方案1】:

所以,除了我相信的一个之外,您几乎可以选择所有可能的选项。 如果按照 Littlefoot 的回答不适合您,您可以尝试使用虚拟列。

  1. 创建 user_work 表:

    create table user_work(user_work_id number, user_id number, status number);
    
  2. 创建存储函数来计算用户的状态:

     create or replace function get_status(p_user_id number) return number deterministic
     is
       active_cnt number := 0;
     begin
       select count(1)
         into active_cnt
         from user_work uw
        where uw.user_id = p_user_id;
    
       if active_cnt > 0 then
         return 1;
       else
         return 0;
       end if;
     end;
    
  3. 创建表user_tab如下:

     create table user_tab(user_id number, status generated always as (get_status(user_id)) virtual);
    

现在,让我们测试一下:

insert into user_tab(user_id) values(1);
insert into user_tab(user_id) values(2);
insert into user_work(user_work_id, user_id, status) values(1, 1, 1);

select * from user_tab;
user_id status
1 1
2 0

【讨论】:

    【解决方案2】:

    我认为你可以通过statement trigger 实现这一点,这样的事情应该可以达到目的。当然,你应该从一个清理点开始,这意味着首先你需要将user表中的所有值更新为user_work表的最新状态。

    我也相信@Littlefoot 声明是正确的,在两个表中保留相同的字段绝不是一个好主意。

    我在这里给你的是一种解决方案,可以使用 user_work 表中的更改或新条目来维护用户表中的状态。我想这是你要求的。

    让我们想象一下这种情况(我为表格使用了不同的名称)

    SQL> create table user_names ( user_id number, username varchar2(1) , status varchar2(1) ) ;
    
    Table created.
    
    SQL> insert into user_names values ( 1 , 'A' , 1 );
    
    1 row created.
    
    SQL> insert into user_names values ( 2 , 'B' , 1 );
    
    1 row created.
    
    SQL> create table user_work ( user_work_id number, user_id number, status varchar2(1) ) ;
    
    Table created.
    

    在这种情况下,user_work 表中还没有行,所以让我们创建语句触发器来更新或插入

    SQL> create or replace trigger upd_status_user 
    after insert or update on user_work
    begin 
        merge into user_names t 
        using ( select * from user_work ) s 
        on ( t.user_id = s.user_id ) 
        when matched then 
        update set t.status = s.status 
        where 
        s.user_work_id = ( select max(user_work_id) from user_work s where t.user_id = s.user_id ) ;
    end;
    /
    
    Trigger created.
    
    SQL>
    

    现在我们测试一下

    SQL> insert into user_work values ( 100 , 1 , 1 );
    
    1 row created.
    
    SQL> commit ;
    
    Commit complete.
    
    SQL> select * from user_names ;
    
       USER_ID U S
    ---------- - -
             1 A 1
             2 B 1
    
    SQL> insert into user_work values ( 101 , 1 , 0 );
    
    1 row created.
    
    SQL> commit ;
    
    Commit complete.
    
    SQL> select * from user_names ;
    
       USER_ID U S
    ---------- - -
             1 A 0
             2 B 1
    
    SQL> insert into user_work values ( 102 , 1 , 1 ) ;
    
    1 row created.
    
    SQL> commit ;
    
    Commit complete.
    
    SQL>  select * from user_names ;
    
       USER_ID U S
    ---------- - -
             1 A 1
             2 B 1
    

    当我在 user_work 表中插入新记录时,您可以看到 user_names 表(您的用户表)的变化,保持最新状态。

    如果我更新,它会发生同样的情况

    SQL> update user_work set status = 0 where user_work_id=102 ;
    
    1 row updated.
    
    SQL> commit ;
    
    Commit complete.
    
    SQL> select * from user_names ;
    
       USER_ID U S
    ---------- - -
             1 A 0
             2 B 1
    

    【讨论】:

    • @Pointer,你有什么意见吗?
    • 投票给你的答案我会尝试在我的例子中实现。
    • 谢谢@Pointer。如果解决了您的问题,请采纳答案
    【解决方案3】:

    一个选项,如果时间不紧迫,您可以创建一个工作以每小时检查一次并设置如下状态:

    BEGIN
      DBMS_SCHEDULER.CREATE_JOB(JOB_NAME        => 'job_name',
                                JOB_TYPE        => 'PLSQL_BLOCK',
                                JOB_ACTION      => 'BEGIN
    UPDATE USER
       SET STATUS = 0
     WHERE USER_ID NOT IN (SELECT USER_ID FROM USER_WORK W WHERE STATUS = 1);
    COMMIT;
     END;',
                                START_DATE      => SYSTIMESTAMP,
                                REPEAT_INTERVAL => 'freq=hourly; byminute=0; bysecond=0;',
                                ENABLED         => TRUE);
    END;
    

    注意:您也可以使用触发器,但我认为状态应存储在一个地方,并应在需要时直接更新特定操作

    【讨论】:

      【解决方案4】:

      在我看来,这不是一个好主意,因为您将status 信息保存在两个表中(所以再见规范化)。如果您必须知道某人的状态,请查询它。

      示例表:

      SQL> select * from t_user;
      
         USER_ID USERNAM
      ---------- -------
               1 Little
               2 Foot
               3 Pointer     --> doesn't have STATUS in USER_WORK
      
      SQL> select * from user_work;
      
      USER_WORK_ID    USER_ID     STATUS
      ------------ ---------- ----------
               100          1          0
               101          1          1   --> last status for USER_ID = 1 is 1
               102          2          1   --> last (and only) status for USER_ID = 2 is 1
      
      SQL>
      

      查询(假设最后一个状态(按USER_WORK_ID降序排序是某人的当前状态;如果USER_WORK表中没有行,则状态=0)。

      SQL> with temp as
        2    (select w.user_id, w.status,
        3            row_number() over (partition by w.user_id order by w.user_work_id desc) rn
        4     from user_work w
        5    )
        6  select u.user_id, u.username, nvl(t.status, 0) status
        7  from t_user u left join temp t on t.user_id = u.user_id
        8                                and t.rn = 1;
      
         USER_ID USERNAM     STATUS
      ---------- ------- ----------
               1 Little           1
               2 Foot             1
               3 Pointer          0
      
      SQL>
      

      或者,一种简单的方法,创建一个视图

      SQL> create or replace view v_user_status as
        2  with temp as
        3    (select w.user_id, w.status,
        4            row_number() over (partition by w.user_id order by w.user_work_id desc) rn
        5     from user_work w
        6    )
        7  select u.user_id, u.username, nvl(t.status, 0) status
        8  from t_user u left join temp t on t.user_id = u.user_id
        9                                and t.rn = 1;
      
      View created.
      
      SQL> select * From v_user_status;
      
         USER_ID USERNAM     STATUS
      ---------- ------- ----------
               1 Little           1
               2 Foot             1
               3 Pointer          0
      
      SQL>
      

      【讨论】:

      • 我同意这不应该被冗余存储。当我读到它时,它甚至听起来好像是关于是否拥有 any user_work.status = 1,即不是 last 一个。这将使查询更加简单:select user,_id, max(status) from user_work group by user_id.
      • 对,@Thorsten。我认为状态会随着时间而变化,最后一个状态是我们想知道的。不过,如果是“我怀孕了。现在我没有”。并且您想知道“我曾经怀孕过吗?”,那么 MAX(STATUS) 就可以了。不要忘记将该表与 T_USER 表进行外部连接,因为我(作为一个男性)没有怀孕并且可能永远不会怀孕 :),所以我的状态将为 0。
      猜你喜欢
      • 2021-04-19
      • 1970-01-01
      • 2021-11-26
      • 2017-09-16
      • 1970-01-01
      • 2021-01-19
      • 2019-02-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多