Производительность Oracle и память

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

Запрос 1:

SELECT DISTINCT a.no,
   a.id1            ,
   a.id2
   FROM  tbl_b b         ,
         tbl_a a ,
         tbl_c c    ,
         tbl_d d
   WHERE (
          b.id1              = a.id1
      AND a.id1              = c.id1
      AND upper(c.flag)      = 'Y'
      AND c.id1              = d.id1
   )
   OR (
          b.id2              = a.id2
      AND a.id2              = c.id2
      AND upper(c.flag)      = 'Y'
      AND c.id2              = d.id2
)
AND d.id3 = 10

Здесь таблицы b и d — это очень большие таблицы, содержащие от 500 000 до миллионов строк, тогда как таблица a относительно меньше.

Мое требование состоит в том, чтобы выбрать только те записи из таблицы a, чей идентификатор (id1 или id2) доступен в таблицах b, c, d, а также удовлетворяет некоторым другим условиям.

Альтернативный запрос, который у меня есть,

Запрос 2:

SELECT DISTINCT a.no,
   a.id1             ,
   a.id2
   FROM  tbl_a a
   where exists ( select a.id1, a.id2 from           
          tbl_c c where ((a.id1 = c.id1 or a.id2 = c.id2)
          AND upper(c.active_flag) = 'Y'))
   and exists ( select a.id1, a.id2 from 
          tbl_b b where  b.id1 = a.id1 or b.id2 = a.id2)
   and exists ( select  a.id1, a.id2 from tbl_d d
               where (a.id1 = d.id1 or a.id2 = d.id2)
               AND d.id3 = 10)

Что лучше с точки зрения производительности? Я понимаю, что второй запрос занимает меньше места, чем первый. Но как выбрать лучший?


person Community    schedule 15.07.2009    source источник


Ответы (3)


Чтобы выбрать лучший, нужно попробовать оба с реалистичными данными и посмотреть, какой из них работает лучше всего. Если это логически эквивалентные запросы, то вполне вероятно, что оптимизатор все равно выдаст один и тот же план для обоих, что вы сможете увидеть с помощью AUTOTRACE или TKPROF или подобных инструментов.

person Tony Andrews    schedule 15.07.2009
comment
Привет, Тони, моя ошибка заключалась в том, что я пропустил скобки. Теперь я обновил запрос, он ((a или b) и c) в моем состоянии. - person ; 15.07.2009
comment
Хорошо, я удалил свои комментарии о скобках. Я также счастлив теперь, что два запроса логически эквивалентны. - person Tony Andrews; 15.07.2009

Недостаточно информации, чтобы дать вам четкий ответ. Есть индексы? Для TBL_C какой процент записей имеет FLAG = 'Y' и сколько записей находится в этой таблице? Для таблицы TBL_D сколько записей с ID3 = 10? Является ли 10 жестко закодированным значением или это может быть любое заданное значение (и вы будете использовать переменные связывания - верно?)

Тони прав, используя autotrace и tkprof, чтобы увидеть, какой из них выполняет наименьший объем работы. Я обычно начинаю с получения SQL_PLAN для каждого и просто запускаю их, если стоимость соответствует моим ожиданиям. С количеством данных и правильными индексами ни один из них не должен работать более секунды или двух.

person user60890    schedule 15.07.2009
comment
Информации достаточно, чтобы предположить, какие индексы необходимы. - person jva; 15.07.2009
comment
Уверен в этом? Насколько велики столы? Мы знаем о количестве записей, но не знаем, насколько они велики. Установлено ли для полей FLAG значение «да» в 1 или 99 процентах записей? То же самое для TBL_D? Я столкнулся с гораздо большим количеством проблем с запросами, использующими индексы, когда они должны использовать полное сканирование, а не наоборот. Индексы не зло, но и не все решают. - person user60890; 16.07.2009

Обновление:

Подробнее о производительности см. в этой статье в моем блоге:

Это будет наиболее эффективным:

SELECT  a.no,
        a.id1,
        a.id2
FROM    tbl_a a
WHERE   EXISTS
        (
        SELECT  1
        FROM    tbl_b b
        WHERE   b.id1 = a.id1
        )
        AND EXISTS
        (
        SELECT  1
        FROM    tbl_c c
        WHERE   c.id1 = a.id1
                AND UPPER(c.flag) = 'Y'
        )
        AND EXISTS
        (
        SELECT  1
        FROM    tbl_d d
        WHERE   d.id1 = a.id1
                AND d.id3 = 10
        )
UNION
SELECT  a.no,
        a.id1,
        a.id2
FROM    tbl_a a
WHERE   EXISTS
        (
        SELECT  1
        FROM    tbl_b b
        WHERE   b.id2 = a.id2
        )
        AND EXISTS
        (
        SELECT  1
        FROM    tbl_c c
        WHERE   c.id2 = a.id2
                AND UPPER(c.flag) = 'Y'
        )
        AND EXISTS
        (
        SELECT  1
        FROM    tbl_d d
        WHERE   d.id2 = a.id2
                AND d.id3 = 10
        )

Oracle не слишком хорош в оптимизации OR условий.

Разделение запросов должно помочь оптимизатору использовать два разных плана выполнения для каждой из OR частей.

Вы должны создать следующие индексы:

tbl_b (id1)
tbl_b (id2)
tbl_c (id1, UPPER(flag))
tbl_c (id2, UPPER(flag))
tbl_d (id1, id3)
tbl_d (id2, id3)

Обратите внимание на функциональный индекс на UPPER(flag), вы должны создать его именно таким образом.

person Quassnoi    schedule 15.07.2009
comment
Вы изменили запрос - ('y','Y') != ('Y') Union вызывает сортировку возвращаемых наборов результатов. Первый выбор отсутствует И d.id3 = 10 - person user60890; 15.07.2009
comment
@glenn: в исходном запросе указано UPPER(flag) = 'Y', что эквивалентно flag IN ('y', 'Y') - person Quassnoi; 15.07.2009
comment
@glenn: UNION не обязательно вызывает сортировку, это зависит от метода, выбранного оптимизатором: SORT UNIQUE или HASH UNIQUE, последний не сортирует - person Quassnoi; 15.07.2009