查找两个日期之间是否存在N个或多个N条记录。如果是这样更新这些记录而不进行额外的表扫描

时间:2018-02-22 12:48:38

标签: sql oracle plsql

 ID Name Time Status
 1   A    5     0
 2   B    6     0
 3   C    7     0
 4   D    10    0

让我们说N = 2,我需要找出时间5和10之间是否存在2条记录。在这种情况下,id为2&的记录。 3.如果存在记录,我必须将这些记录的状态更新为1。

可能的选择。 BULK将id收集到一些临时PLSQL集合中,如果该集合的大小大于2,则更新这些ID的状态。

有没有更好的方法而没有两次击中桌子?

3 个答案:

答案 0 :(得分:2)

在PL / SQL中,无论如何都可以进行更新,如果sql%rowcount不是2,则可以撤消更新。

create or replace procedure p1
    ( p_start date
    , p_end   date )
as
begin
    savepoint before_update;

    update demo set indicator = 'Y'
    where  dt between p_start and p_end
    and    rownum < 4;  -- limit here as we will be rolling back if 3 or more anyway

    dbms_output.put('Updated ' || sql%rowcount || ' row(s)');

    if sql%rowcount <> 2 then
        rollback to before_update;
        dbms_output.put_line(' but rolled back');
    else
        dbms_output.new_line;
    end if;
end p1;

演示(Oracle 12.1):

create table demo (dt date not null, indicator varchar2(1));

insert all
    into demo values (date '2018-01-01', null)
    into demo values (date '2018-02-01', null)
    into demo values (date '2018-03-01', null)
    into demo values (date '2018-03-09', null)
    into demo values (date '2018-04-01', null)
    into demo values (date '2018-05-01', null)
select * from dual;


call p1(date '2018-01-01', date '2018-06-01');
-- Updated 3 row(s) but rolled back

call p1(date '2018-03-01', date '2018-03-31');
-- Updated 2 row(s)

答案 1 :(得分:1)

我会在这里使用merge

merge into tbl 
  using (select id, count(1) over () cnt from tbl where 5 < time and time < 10) src
  on (tbl.id = src.id and cnt >= 2)
  when matched then update set status = 1;

..或者如果您出于某种原因更喜欢PLSQL和bulk collect

declare 
    v_ids sys.odcinumberlist;
begin 
    select id
      bulk collect into v_ids
      from (select id, count(1) over () cnt from tbl where 5 < time and time < 10)
      where cnt >= 2;

    forall i in 1..v_ids.count
      update tbl set status = 1 where id = v_ids(i);
end;

示例数据:

create table tbl(id, name, time, status) as 
    select 1, 'A',  5, 0 from dual union all
    select 2, 'B',  6, 0 from dual union all
    select 3, 'C',  7, 0 from dual union all
    select 4, 'D', 10, 0 from dual;

merge之后的结果或执行阻止:

    ID NAME       TIME     STATUS
------ ---- ---------- ----------
     1 A             5          0
     2 B             6          1
     3 C             7          1
     4 D            10          0

答案 2 :(得分:0)

这对我来说是一个奇怪的要求。但您可以使用where子句中的子查询来执行此操作:

update t
    set status = 1
    where t.time > 5 and t.time < 10 and
          (select count(*) from t where t.time > 5 and t.time < 10) >= 2