2011-12-23 2 views
2

У меня есть 2 стола. Таблица «Accs» содержит 9 миллионов строк (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) 

Этот запрос занимает много времени , Правильно ли я это делаю?

+0

Я сомневаюсь, что вы могли бы ускорить это. многие ваши соединения выполняются по вычисленным результатам, что означает, что индексы не могут использоваться. –

+0

Есть ли другой более быстрый способ сделать то же самое? – DmitryB

+3

Храните эти вычисленные поля в производных полях (что несколько денормирует тики), но позволяет использовать индексы. –

ответ

2

Первая попытка

Не могу проверить это, не имея фактическую структуру таблицы и данные, но у меня сделал несколько незначительных изменений, которые иногда оказывают большое влияние.

Прежде всего, я сменил LEFT JOIN на with на INNER JOIN. Поскольку вы используете значения a в предложении WHERE, он будет функционировать как 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 
+0

Согласно рекомендации Marc B, я разделил поле «acc_id» на 2 поля.Что касается условия «Где» таблицы «Аксы» - я помещаю его в подзапрос. – DmitryB

0

Вы могли бы попытаться создать индексы на базе функций, как этот один:

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

Это было первое, что я сделал, но это не помогло. – DmitryB

Смежные вопросы