oracle sql - 查找日期(开始/结束列)重叠的条目

时间:2015-06-13 18:06:42

标签: sql oracle date range

所以数据是这样的:

ID | START_DATE       | END_DATE         |  UID  | CANCELED
-------------------------------------------------
44 | 2015-10-20 22:30 | 2015-10-20 23:10 | 'one' |
52 | 2015-10-20 23:00 | 2015-10-20 23:30 | 'one' |
66 | 2015-10-21 13:00 | 2015-10-20 13:30 | 'two' | 

这些条目超过10万。

我们可以看到第二个条目的start_date与第一个条目的end_date重叠。当日期重叠时,具有较低ID的条目应在“取消”列中标记为真。

我尝试了一些查询,但是他们花了很长时间,所以即使他们工作我也不确定。此外,我想覆盖所有重叠的案例,所以这似乎也减慢了这一点。

我是负责使用pl / sql

插入/更新这些条目的人
update table set column = 'value' where ID = '44';
   if sql%rowcount = 0 
       then insert values(...)
   end if

所以我可以在这一步中做到这一点。但是所有的表都是使用动态创建的一个大的pl / sql进行更新/插入的,其中所有行都会更新或者新的行被插入,所以这似乎再次变慢。

在所有sql'方言'oracle中,我有机会使用它是最神秘的。想法?

编辑:我忘了一个重要的细节,还有一个要匹配的列(UID),上面更新

4 个答案:

答案 0 :(得分:1)

我认为以下更新应该有效:

update tbl
   set cancelled = 'TRUE'
 where t_id in (select t_id
                  from tbl t
                 where exists (select 1
                          from tbl x
                         where x.t_id > t.t_id
                           and x.start_date <= t.end_date));

小提琴: http://sqlfiddle.com/#!4/06447/1/0

如果表格非常大,您可能最好使用CTAS(创建表格为选择)查询创建新表格,您可以使用nologging选项,以避免必须写入撤消日志。当您执行类似于现在的更新时,您正在将更改写入Oracle的撤消日志,以便在提交事务之前,您可以选择回滚。这增加了开销。因此,带有nologging的CTAS查询可能会运行得更快。这是这种方法的一种方式:

create table new_table nologging as
with sub as
 (select t_id,
         start_date,
         end_date,
         'TRUE' as cancelled
    from tbl t
   where exists (select 1
            from tbl x
           where x.t_id > t.t_id
             and x.start_date <= t.end_date))
select *
  from sub
union all
select   t.*
      from tbl t
 left join sub s
        on t.t_id = s.t_id
      where s.t_id is null;

小提琴: http://sqlfiddle.com/#!4/c6a29/1

答案 1 :(得分:1)

我会从这个查询开始:

update table t
    set cancelled = true
    where exists (select 1
                  from table t2
                  where t.end_date > t2.start_date and
                        t.uid = t2.uid and
                        t.id < t2.id
                 )

table(uid, start_date, id)上的索引可能会有所帮助。

注意:创建表格时,这可能更容易 ,因为您可以使用lag()

答案 2 :(得分:1)

这将在没有动态查询或相关子查询的情况下完成,但它为 MERGE INTO Table1 USING ( with q0 as( select rownum fid, id, start_date from( select id, start_date from table1 union all select 999999 id, null start_date from dual order by id ) ), q1 as ( select rownum fid, id, end_date from( select -1 id, null end_date from dual union all select id, end_date from table1 order by id ) ) select q0.fid, q1.id, q0.start_date, q1.END_DATE, case when (q0.start_date < q1.END_DATE) then 1 else 0 end canceled from q0 join q1 on (q0.fid = q1.fid) ) ta ON (ta.id = Table1.id) WHEN MATCHED THEN UPDATE SET Table1.canceled = ta.canceled; 子句消耗了一些内存:

with

带有别名select的内部ta "FID"|"ID"|"START_DATE" |"END_DATE" |"CANCELED" --------------------------------------------------------- 1 |-1 |20/10/15 22:30:00| |0 2 |44 |20/10/15 23:00:00|20/10/15 23:10:00|1 3 |52 |21/10/15 13:00:00|20/10/15 23:30:00|0 4 |66 | |20/10/15 13:30:00|0 语句会产生以下结果:

merge

然后在{{1}} v中使用它而没有任何相关查询。使用SQLDeveloper进行了测试和工作。

答案 3 :(得分:1)

您可以使用BULK COLLECT INTOFORALL来减少过程中的上下文切换:

SQL Fiddle

Oracle 11g R2架构设置

CREATE TABLE test ( ID, START_DATE, END_DATE, CANCELED ) AS 
          SELECT 44, TO_DATE( '2015-10-20 22:30', 'YYYY-MM-DD HH24:MI' ), TO_DATE( '2015-10-20 23:10', 'YYYY-MM-DD HH24:MI' ), 'N' FROM DUAL
UNION ALL SELECT 52, TO_DATE( '2015-10-20 23:00', 'YYYY-MM-DD HH24:MI' ), TO_DATE( '2015-10-20 23:30', 'YYYY-MM-DD HH24:MI' ), 'N' FROM DUAL
UNION ALL SELECT 66, TO_DATE( '2015-10-21 13:00', 'YYYY-MM-DD HH24:MI' ), TO_DATE( '2015-10-21 12:30', 'YYYY-MM-DD HH24:MI' ), 'N' FROM DUAL
/

CREATE PROCEDURE updateCancelled
AS
  TYPE ids_t IS TABLE OF test.id%TYPE INDEX BY PLS_INTEGER;
  t_ids   ids_t;
BEGIN
  SELECT ID
  BULK COLLECT INTO t_ids
  FROM   (
          SELECT ID,
                 END_DATE,
                 LEAD( START_DATE ) OVER ( ORDER BY START_DATE ) AS NEXT_START_DATE
          FROM   TEST )
  WHERE  END_DATE > NEXT_START_DATE;

  FORALL i IN 1 .. t_ids.COUNT
    UPDATE TEST
    SET    CANCELED = 'Y'
    WHERE  ID = t_ids(i);
END;
/

BEGIN
  updateCancelled();
END;
/

查询1

SELECT * FROM TEST

<强> Results

| ID |                START_DATE |                  END_DATE | CANCELED |
|----|---------------------------|---------------------------|----------|
| 44 | October, 20 2015 22:30:00 | October, 20 2015 23:10:00 |        Y |
| 52 | October, 20 2015 23:00:00 | October, 20 2015 23:30:00 |        N |
| 66 | October, 21 2015 13:00:00 | October, 21 2015 12:30:00 |        N |

或者作为单个SQL语句:

UPDATE TEST
SET    CANCELED = 'R'
WHERE  ID IN ( SELECT ID
               FROM ( SELECT ID,
                             END_DATE,
                             LEAD( START_DATE )
                               OVER ( ORDER BY START_DATE )
                               AS NEXT_START_DATE
                      FROM TEST )
               WHERE END_DATE > NEXT_START_DATE )