【问题标题】:How to optimize next oracle query?如何优化下一个 oracle 查询?
【发布时间】:2011-12-23 05:42:32
【问题描述】:

我有 2 张桌子。表“Accs”包含 900 万行(3 列:acc_id、月、年)。 首先我需要提取包含帐号部分条目的记录, 然后在这些记录中找到完全匹配,如果没有-则第一个部分匹配

WITH t AS (
  SELECT a.acc_id,
         t1.as,
         t1.cust,
         t1.curr,
         t1.code,
         t1.depart,
         t1.sdate,
         t1.stype,
         t1.amount,
         t1.s_id
  FROM table1 t1 
  LEFT JOIN Accs a 
  ON SUBSTR(a.acc_id,7,12)=t1.curr||LPAD(t1.code,4,'0')||LPAD(t1.depart,3,'0')
  WHERE t1.sdate='20.11.2011' AND t1.stype='A' AND a.month=11 ANd a.year=2011)
SELECT MAX(t.s_id),
       (CASE WHEN t.as='000000' 
             THEN (CASE WHEN ac2.acc_id IS NOT NULL THEN ac2.acc_id ELSE t.acc_id END) 
             ELSE t.cust||t.curr||LPAD(t.code,4,'0')||LPAD(t.depart,3,'0') END) acc_id
FROM t 
LEFT JOIN (SELECT t.acc_id FROM t) ac2 
ON SUBSTR(ac2.acc_id,1,6)='000'||LPAD(t.depart,3,'0')
GROUP BY      
       (CASE WHEN t.as='000000' 
             THEN (CASE WHEN ac2.acc_id IS NOT NULL THEN ac2.acc_id ELSE t.acc_id END) 
             ELSE t.cust||t.curr||LPAD(t.code,4,'0')||LPAD(t.depart,3,'0') END)

此查询需要很长时间。我这样做是否正确?

【问题讨论】:

  • 我怀疑你能否加快速度。您的很多连接都是在计算结果上完成的,这意味着无法使用索引。
  • 将这些计算字段存储在派生字段中(这在一定程度上使thigns 非规范化),但允许使用索引。
  • 你为什么这样做:LEFT JOIN (SELECT t.acc_id FROM t) ac2 ON ... 如果你只是加入 t 并给它起别名,它可能会更快。不需要额外的选择。
  • 你怎么能读到这样的查询。 Oracle 对标识符的 30 个字符的限制是荒谬的,但这并不意味着必须将它们全部截断为 4 个。:)
  • 请贴出执行计划、表定义和涉及的表上定义的索引

标签: sql oracle plsql oracle10g


【解决方案1】:

第一次尝试

如果没有实际的表结构和数据,无法对其进行真正的测试,但我做了一些有时会产生重大影响的小改动。

首先,我将with 部分中的LEFT JOIN 更改为INNER JOIN。由于您在 WHERE 子句中使用了 a 的值,因此无论如何它都可以用作 inner join,而且通常要快得多,尤其是在有这么多数据和适当索引的情况下。

我将内部的CASE 更改为NVL,因为它本质上就是这样做的。不知道这样会不会加快速度。

将字符串连接从外部查询移至with 部分。

这些只是很小的变化,可能会产生从没有到相当大的影响。至少您可以在更改表结构之前尝试这些方法,尽管这样做可能是个好主意。

WITH t AS 
(
  SELECT 
    a.acc_id,
    t1.as,
    t1.cust,
    t1.curr,
    t1.code,
    t1.depart,
    t1.sdate,
    t1.stype,
    t1.amount,
    t1.s_id,
    t1.cust || t1.curr || LPAD(t1.code, 4, '0') || LPAD(t1.depart, 3, '0') as groupfield
  FROM 
    table1 t1 
    INNER JOIN Accs a
      ON SUBSTR(a.acc_id, 7, 12) = t1.curr || LPAD(t1.code, 4, '0') || LPAD(t1.depart, 3, '0')
  WHERE 
    t1.sdate = '20.11.2011' AND t1.stype = 'A' AND a.month = 11 ANd a.year = 2011
)
SELECT
  MAX(t.s_id),
  (CASE WHEN t.as = '000000' THEN 
    NVL(ac2.acc_id, t.acc_id)
  ELSE
    t.groupfield
  END) acc_id
FROM 
  t 
  LEFT JOIN t ac2 on ac2 
    ON SUBSTR(ac2.acc_id, 1, 6) = '000' || LPAD(t.depart, 3, '0')
GROUP BY      
  (CASE WHEN t.as = '000000' THEN 
    NVL(ac2.acc_id, t.acc_id)
  ELSE
    t.groupfield
  END)

第二次尝试

在对您的查询进行了更多研究之后,我想知道您是否不能只将其设为单个/简单查询而不是使用 with。我认为通过首先内部加入 Accs,然后在额外条件下再次离开加入 Accs,你真的是一个很好的方法。

  SELECT 
    MAX(t1.s_id) AS s_id,
    CASE WHEN t.as = '000000' THEN
      NVL(a2.acc_id, a.acc_id)
    ELSE
      t1.cust || t1.curr || LPAD(t1.code, 4, '0') || LPAD(t1.depart, 3, '0')
    END AS acc_id
  FROM 
    table1 t1 
    INNER JOIN Accs a
      ON SUBSTR(a.acc_id, 7, 12) = t1.curr || LPAD(t1.code, 4, '0') || LPAD(t1.depart, 3, '0')
      AND a.month = 11 AND a.year = 2011
    LEFT JOIN Accs a2
      ON SUBSTR(a2.acc_id, 7, 12) = t1.curr || LPAD(t1.code, 4, '0') || LPAD(t1.depart, 3, '0')
      AND a2.month = 11 AND a2.year = 2011
      AND SUBSTR(a2.acc_id, 1, 6) = '000' || LPAD(t1.depart, 3, '0')
  WHERE 
    t1.sdate = '20.11.2011' AND t1.stype = 'A'
  GROUP BY 
    CASE WHEN t.as = '000000' THEN
      NVL(a2.acc_id, a.acc_id)
    ELSE
      t1.cust || t1.curr || LPAD(t1.code, 4, '0') || LPAD(t1.depart, 3, '0') as groupfield
    END AS acc_id

【讨论】:

  • 根据 Marc B 的建议,我将“acc_id”字段分为2个字段。关于“Accs”表的条件“Where”——我把它放在一个子查询中。
【解决方案2】:

您可以尝试创建像这样的基于函数的索引:

create index xxx on accs (SUBSTR(a.acc_id,7,12));

【讨论】:

  • 这是我做的第一件事,但没有帮助。
猜你喜欢
  • 2017-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-26
  • 2011-01-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多