V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
ZEOH
V2EX  ›  问与答

关于重复性校验的一条 SQL 优化问题请教

  •  
  •   ZEOH · 2017-11-16 17:12:11 +08:00 · 1728 次点击
    这是一个创建于 2564 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Oracle 数据库一张表有如下字段:
    FOO
    | 字段     | 类型      | 说明                 |
    | LOCATION    | VARCHAR2(512) | 地点,可以为多个,格式如:北京|上海  |
    | STARTTIME   | VARCHAR2(20) | 开始时间,格式为:YYYYMMDD24HHMISS |
    | ENDTIME    | VARCHAR2(20) | 结束时间,格式为:YYYYMMDD24HHMISS |
    | FILED_A     | VARCHAR2(30) | 字段 A,有可能为空           |
    | FILED_B     | VARCHAR2(30) | 字段 B,有可能为空           |
    | FILED_C     | VARCHAR2(30) | 字段 C,有可能为空           |
    | FILED_D     | VARCHAR2(30) | 字段 D,有可能为空           |

    需求是用文件导入的方式批量对这张表进行新增操作,但导入时需要对数据执行一系列校验。
    其中有一个步骤是 重复性校验。若同时满足以下条件,则说明导入的数据已经存在,校验失败:

    1. FILED_A、FILED_B、FILED_C、FILED_D 四个字段完全相同;
    2. 导入数据的时间区间( STARTTIME ~ ENDTIME )与已经存在的数据时间有交叉(如数据 A [20171101 ~ 20171103]与数据 B [20171102 ~ 20171104] 视为交叉;
    3. 导入数据的 LOCATION 与已经存在的数据有交叉(如数据 A [北京|上海] 与数据 B[上海|杭州] 视为交叉)。

    原先的方案是在内存中比较,但由于导入的数据往往较大,如果取一条数据查一次数据库看存不存在,5000 条数据就要查 5000 次,性能比较差。 于是考虑把待校验的数据放在一张临时表中,一次性对所有数据进行对比。

    临时表与正式表结构基本一致,但多了下面一个字段:

    TEMP_FOO
    | 字段     | 类型     | 说明   |
    | CHECKRESULT | VARCHAR2(20) | 校验结果 |

    目前写出的重复性校验 SQL 如下(如果临时表 TEMP_FOO 待校验的数据已经存在正式表 FOO 中,则将其校验结果字段更新为 Repeat ):

    UPDATE TEMP_FOO t
    SET t.CHECKRESULT = 'Repeat'
    WHERE EXISTS (
        SELECT 1 FROM FOO o
        WHERE regexp_like(o.LOCATION, t.LOCATION)
        AND o.STARTTIME >= concat(to_char(to_date(t.STARTTIME,'yyyymmddhh24miss'),'yyyymmdd'),'000000')
        AND o.STARTTIME < concat(to_char(to_date(t.STARTTIME,'yyyymmddhh24miss')+1,'yyyymmdd'),'000000')
        AND o.ENDTIME >= concat(to_char(to_date(t.ENDTIME,'yyyymmddhh24miss'),'yyyymmdd'),'000000')
        AND o.ENDTIME < concat(to_char(to_date(t.ENDTIME,'yyyymmddhh24miss')+1,'yyyymmdd'),'000000')
        AND nvl(t.FILED_A, '0') = nvl(o.FILED_A, '0')
        AND nvl(t.FILED_B, '0') = nvl(o.FILED_B, '0')
        AND nvl(t.FILED_C, '0') = nvl(o.FILED_C, '0')
        AND nvl(t.FILED_D, '0') = nvl(o.FILED_D, '0')
        )
    

    现在有几个问题想请教大家:

    1. 当导入数据量很大(超过 5000 条),该条 SQL 执行效率仍然很低,请问是否有办法优化?
    2. 有时候 WHERE 子句中,FILED_A、FILED_B、FILED_C、FILED_D 四个条件已经能够保证查询结果为 0 了,为什么 oracle 还是缓慢地执行了全部筛选条件?
    3. 不限于在数据库中做对比,是否还有其他方案可以高效地进行重复性校验?

    有任何一点建议都可以提出,非常感谢!

    5 条回复    2017-11-17 10:10:05 +08:00
    canbingzt
        1
    canbingzt  
       2017-11-16 17:37:55 +08:00
    感觉把校验内容单独建表是不是效率更高
    1.建一张时间表,保存已有的时间,你的例子时间只到天,那么就把所有存在的天保存起来,不保存时间段
    2.建一张地点表,保存已有的地点
    picone
        2
    picone  
       2017-11-16 19:11:11 +08:00
    时间方面,字段类型改成是 TIMESTAMP 或 DATE。

    如果要放在内存做验证也不是不可以,做一个链表存放时间返回,详细的可以参考操作系统的内存管理是怎么做的。
    ZEOH
        3
    ZEOH  
    OP
       2017-11-17 09:26:06 +08:00
    @canbingzt 不是很清楚您的意思,能否仔细说明下?
    ZEOH
        4
    ZEOH  
    OP
       2017-11-17 09:26:34 +08:00
    @picone 好的,我去看下
    picone
        5
    picone  
       2017-11-17 10:10:05 +08:00
    @ZEOH #4 我说的是指时间校验那方面,可以参考一下内存管理, 他会标识哪一块已用,哪一块未用,合并连续块,用这种方法就不用很大的内存了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2663 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 05:56 · PVG 13:56 · LAX 21:56 · JFK 00:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.