【问题标题】:oracle sql - finding entries with dates (start/end column) overlaporacle sql - 查找日期(开始/结束列)重叠的条目
【发布时间】:2015-08-29 13:50:20
【问题描述】:

所以数据是这样的:

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' | 

这些条目超过 100k。

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

我尝试了一些查询,但它们需要很长时间,所以我不确定它们是否有效。另外我想涵盖所有重叠的情况,所以这似乎也减慢了速度。

我是负责使用 pl/sql 插入/更新这些条目的人

update table set column = 'value' where ID = '44';
   if sql%rowcount = 0 
       then insert values(...)
   end if

所以我也许可以在这一步中做到这一点。但是所有表都使用动态创建的一个大 pl/sql 更新/插入,其中所有行要么更新要么插入新行,所以这似乎又变慢了。

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

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

【问题讨论】:

    标签: sql oracle date range


    【解决方案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(create table as select)查询创建一个新表,您可以在其中使用nologging 选项,从而避免写入撤消操作日志。当您像现在这样执行更新时,您正在将更改写入 Oracle 的撤消日志,以便在提交事务之前,您可以选择回滚。这增加了开销。因此,没有记录的 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

    【讨论】:

    • 我认为关联子查询不适合大量数据。
    • 100k 行我不会认为是“大量数据”,至少对于 Oracle 来说不是。
    • @Wernfried 。 . .这真的很难说。 Oracle 有一个很好的优化器,这实际上可能工作得很好。值得一试。
    【解决方案2】:

    我将从这个查询开始:

    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()

    【讨论】:

    • 从这个开始但忘记了一个细节,有问题更新,在我看来我需要补充: where t.uid = t2.uid ?
    • @juzerKicker 。 . .是的,这对性能非常重要。
    • 我最终使用了基于此查询的东西,谢谢,我意识到一切都很慢,因为我正在通过错误的远程链接进行测试...叹息...
    【解决方案3】:

    这将在没有动态查询或相关子查询的情况下完成这项工作,但它会为 with 子句消耗一些内存:

       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;
    

    具有别名ta 的内部with select 语句将产生以下结果:

    "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 v 中使用,没有任何相关查询。使用 SQLDeveloper 测试并运行良好。

    【讨论】:

      【解决方案4】:

      您可以使用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 )
      

      【讨论】:

        猜你喜欢
        • 2020-03-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-02-17
        • 2020-04-30
        • 1970-01-01
        • 1970-01-01
        • 2017-03-31
        相关资源
        最近更新 更多