本帖最后由 shf156184620 于 2010-04-22 17:02:52 编辑

解决方案 »

  1.   

    ABS(N.DURATION - O.DURATION)<=0abs取绝对值,它不可能小于0。ABS(N.DURATION - O.DURATION)=0等价于N.DURATION = O.DURATIONROUND(ABS(N.START_TIME - O.START_TIME) * 24 * 60 * 60) <= 0同理N.START_TIME = O.START_TIME
      

  2.   

     AND ROUND(ABS(N.START_TIME - O.START_TIME) * 24 * 60 * 60) <= 0
     AND ABS(N.DURATION - O.DURATION) <= 0
    这两句在这边不起作用,可以去掉。。
      

  3.   

    ROUND(ABS(N.START_TIME - O.START_TIME) * 24 * 60 * 60)
    就这个表达式来说,在语句中最好写成
    ROUND(ABS(N.START_TIME - O.START_TIME) * 86400)
    不需要oracle再去计算
    当然,你这个语句转化成秒是没有用的。
      

  4.   

    AND ROUND(ABS(N.START_TIME - O.START_TIME) * 24 * 60 * 60) <= 0
    AND ABS(N.DURATION - O.DURATION) <= 0
    0那个位置上其实是个变量,当然0可以不需要这两句了,但不是0呢?
      

  5.   

    加个rownum限制呢? 看看执行计划变了没UPDATE T_YD_USAGE_EVENTS_OLD_30 O
      SET O.BJ = 1
     WHERE EXISTS (SELECT 1
      FROM T_YD_USAGE_EVENTS_NEW_30 N
      WHERE N.THIRD_NUMBER = O.THIRD_NUMBER
      AND ROUND(ABS(N.START_TIME - O.START_TIME) * 24 * 60 * 60) <= 0
      AND ABS(N.DURATION - O.DURATION) <= 0
      AND rownum = 1
    );
      

  6.   

    ROUND(ABS(N.START_TIME - O.START_TIME) * 86400) 
    ABS(N.DURATION - O.DURATION)
    加这个函数索引  临时加更新完再删除就OK
      

  7.   

    首先我也分析下你的SQL是什么目的吧,接楼上
    1、ABS 是绝对值函数,所以它获取的数据肯定是>=0的才,所以ABS(N.DURATION - O.DURATION)<=0等价于ABS(N.DURATION - O.DURATION)=0,那么好比是:N.DURATION = O.DURATION2、接着ROUND(ABS(N.START_TIME - O.START_TIME) * 24 * 60 * 60)<=0 也是等价于 = 0 符号,看楼主的意思是将时间想减得到秒数的,取四舍五入,也就是说,你要的是两个时间差低于0.5秒才在四舍五入时变成0,但是你要知道DATE类型只能支持到秒级别,在秒级别以下,使用DATE做时间差,它会自动去做四舍五入操作,你可以用下面的SQL去做下测试:

    SELECT (SYSDATE - (SYSDATE - 0.5 / (1440 * 60))) * (1440 * 60) FROM DUAL;
    输出 1
    SELECT (SYSDATE - (SYSDATE - 0.49999 / (1440 * 60))) * (1440 * 60) FROM DUAL;
    输出 0

    所以两个DATE类型的时间差小于0.5秒,自动变成0了,而不需要你自己去通过ROUND去转换操作,所以他们还是等价于:
    N.START_TIME = O.START_TIME;然后,不是很明白需求是什么样的,如果后面两个连接可以不要的话,最好不要了,如果业务要求,必须通过这三个条件来判定,那么此时这个SQL肯定是慢的,因为你要跟新几百万条数据,两个表的大小都超过了500万条,现在的目的是要保证数据库的安全,我们此时建议的是分批修改,我相信这么大的表,应该做分区了吧,如果可以按照分区逐个修改是比较好的。修改完一次就COMMIT一次:若存在分区:
    UPDATE T_YD_USAGE_EVENTS_OLD_30  PARTITION(分区名称) NOLOGGING
      SET BJ = 1
     WHERE EXISTS (SELECT 1
      FROM T_YD_USAGE_EVENTS_NEW_30 N
      WHERE N.THIRD_NUMBER = THIRD_NUMBER
       AND N.START_TIME = START_TIME
       AND N.DURATION = DURATION);
      

  8.   

    这么大的数据量,我觉得用hash半连接最好
    UPDATE T_YD_USAGE_EVENTS_OLD_30 O
      SET O.BJ = 1
     WHERE EXISTS (SELECT /*+ hash_sj(o n)*/1
      FROM T_YD_USAGE_EVENTS_NEW_30 N
      WHERE N.THIRD_NUMBER = O.THIRD_NUMBER);
      

  9.   


    是变量啊,呵呵,楼上很多回答都看成0了,我干脆用[:O]代表你传入的参数吧,呵呵,如果是这样的话,的确有点麻烦了,其实这么大的数据量,你开始说的索引基本无用,此时推荐使用要ORACLE去走HASH连接,但是HASH这么大的数据量是怕超HASH_AREA_SIZE的量,所以逐步转移是比较好的,对于SQL来说,除了NOLOGGING减少日志编写以外,我目前只能这样修改一下,对于这部分:AND ROUND(ABS(N.START_TIME - O.START_TIME) * 24 * 60 * 60) <= [:0]上一次可以论证对于时间差来说,你做ROUND操作的用处不太大,无非是要精确到秒去做操作,暂时抛开它就变成:
    AND ABS(N.START_TIME - O.START_TIME) * 24 * 60 * 60 <= [:0]可以发现的是* 24 * 60 * 60 对每行数据都会去做,这个部分不如将它交给变量,每行数据就不会去运算这部分了:
    AND ABS(N.START_TIME - O.START_TIME) <= [:O]/(1440*60)
    另外如果我遇到类似大批量修改,还要关联修改的话,如果有分区表的话,我会先将一个分区和外部同结构的空表进行一次交换:--创建外部同结构的表:
    CREATE TABLE <外部同结构表名称> AS
     SELECT * FROM <分区表名称>
      WHERE 1=2;--此时仅仅进行段名称交换,是瞬间完成的。
    ALTER TABLE <分区表名称> EXCHANGE <分区名称> 
      WITH TABLE <外部一张同结构空表> INCLUDING INDEXES;--然后通过这个已经替换的外部表进行一次:
    INSERT /*+append*/ INTO <分区表> NOLOGGING
     SELECT <对应列,并将BJ对应成立部分改成1>
       FROM 。。--将外部表清空:
    TRUNCATE TABLE <清空外部表名称>;--给分区创建对应的索引信息(分全局索引和分区索引),一般重新编译一次即可。--每个分区数据交换完成后,此时将外部表可以彻底删除掉,当然看你选择了:
    DROP TABLE <外部同结构的表> PURGE;
      

  10.   

    UPDATE T_YD_USAGE_EVENTS_OLD_30 O
      SET O.BJ = 1
     WHERE EXISTS (SELECT 1
      FROM T_YD_USAGE_EVENTS_NEW_30 N
      WHERE N.THIRD_NUMBER = O.THIRD_NUMBER
      AND ROUND(ABS(N.START_TIME - O.START_TIME) * 24 * 60 * 60) <= 0
      AND ABS(N.DURATION - O.DURATION) <= 0);
    先把标红的内容新建一个表:
    create table t_tmp
    as 
    SELECT distinct O.rowid rid
      FROM T_YD_USAGE_EVENTS_NEW_30 N,T_YD_USAGE_EVENTS_OLD_30 O
      WHERE N.THIRD_NUMBER = O.THIRD_NUMBER
      AND ROUND(ABS(N.START_TIME - O.START_TIME) * 24 * 60 * 60) <= 0
      AND ABS(N.DURATION - O.DURATION) <= 0;下面再update 一定快:
    UPDATE /*+ordered use_nl(0)*/ T_YD_USAGE_EVENTS_OLD_30 O
      SET O.BJ = 1
     WHERE rowid in(select rid from t_tmp)