如何让'不存在'更快

时间:2011-10-21 08:44:51

标签: sql oracle oracle9i

我有问题。我的数据库是Oracle 9.这是我的SQL:

SELECT COUNT(distinct A.person_id)
        INTO p_record_count
        FROM A,
             B,
             F,
             T
       WHERE B.position_id = F.position_id **--Normal bussiness association**
         AND T.field2 = A.org_id           **--Normal bussiness association**
         AND F.Organization_Id = A.org_id  **--Normal bussiness association**
         AND A.person_id = F.person_id     **--Normal bussiness association**
         AND F.primary_flag = 'Y'          **--Normal bussiness association**
         and sysdate between F.effective_start_date and
             F.effective_end_date          **--Normal bussiness association**
         AND NOT EXISTS                    --very slow
                (SELECT log.person_id
                FROM cper.ehr_access_log log
               WHERE log.person_id = A.person_id
                 AND log.access_page = 'login.do'
                 AND trunc(log.access_time) BETWEEN
                     to_date(p_startDate, 'yyyy-mm-dd') AND
                     to_date(p_endDate, 'yyyy-mm-dd'))
         AND A.enable_flag = 'Y';            **--Normal bussiness association**

要求:获取未登录的人。

逻辑:得到我能得到的所有人,然后减去登录的人。

我只有一张包含登录记录的表。

表cper.ehr_access_log的记录超过10M。这是一张日志表。这个SQL大约需要30秒。

5秒内可以吗?谢谢你的帮助。

谢谢〜!我会试试。祝周末愉快〜:)

4 个答案:

答案 0 :(得分:3)

  

表cper.ehr_access_log有超过10M条记录。它是一个日志   table.This大约需要30秒。可能在5秒内?

我想说如果不添加一些索引就不可能。但您可能想尝试以下建议

  • 使用查询计划来识别热点。这可能是ehr_access_log所需的表格,您可以使用该参数来说服其他人您需要索引。

  • 尝试使用外部联接替换子查询,正如其他人建议的那样

  • 您已加入BT,但不加上谓词。你真的在查询中需要它们吗?

  • 您可以通过避免trunc来电来节省一些时间(不确定)。作为替代方案,请修改p_startDate一天开始,p_endDate到一天结束

答案 1 :(得分:2)

查询的“不存在”部分称为相关子查询。如果重写为OUTER连接,通常可以获得更好的性能。

模式是

select a from b where not exists (select 1 from c where b.a = c.a)

变为

select a from b left outer join c on b.a = c.a where c.a is null

答案 2 :(得分:1)

为了获得更好的效果,您必须 INDEX cper.ehr_access_log

由于它是一个LOG表,因此请根据 person_id 制定每月计划以索引此表。这将减少一些查询时间。

答案 3 :(得分:1)

我认为您可以通过仅选择DISTINCT log.person_id记录来优化查询,它将减少日志中的记录数量。

您也可以尝试从第一部分开始排除:

SELECT COUNT(distinct A.person_id)
    INTO p_record_count
    FROM A,
         B,
         F,
         T
   LEFT OUTER JOIN cper.ehr_access_log log
     ON log.person_id = A.person_id
        AND log.access_page = 'login.do'
        AND trunc(log.access_time) BETWEEN
        to_date(p_startDate, 'yyyy-mm-dd') AND
        to_date(p_endDate, 'yyyy-mm-dd')
   WHERE B.position_id = F.position_id **--Normal bussiness association**
     AND T.field2 = A.org_id           **--Normal bussiness association**
     AND F.Organization_Id = A.org_id  **--Normal bussiness association**
     AND A.person_id = F.person_id     **--Normal bussiness association**
     AND F.primary_flag = 'Y'          **--Normal bussiness association**
     and sysdate between F.effective_start_date and
         F.effective_end_date          **--Normal bussiness association**
     AND log.person_id IS null
     AND A.enable_flag = 'Y';            **--Normal bussiness association**