2014-12-16 4 views
1

У меня есть три таблицы Address, Item и Item_LocationВыполнение SQL

Address стол:

address_id | street_nm | street_nbr | city | zipcode | ... 
------------------------------------------------------------- 
    3  | Zebra  | 333  | Houston | 77777 | 
    4  | Yak  | 333  | Houston | 77777 | 

Item_Location стол:

item_location_id | address_id 
------------------------------ 
    2    | 3 
    4    | 4 

Item стол:

item_id | location_a (FK_item_location_id) | location_b (FK_item_location_id) 
------------------------------------------------------------------------------- 
1  | 2        | 4 

У меня запущено условие поиска: Find out items at a/b location based on address given. Как вы видели, есть ссылка на местоположение в двух столбцах элемента.

Запрос для поиска является:

select Item.item_id, 
from 
    Item, (select address_id, item_location_id 
     from address, Item_location il 
     where street_nm like '%zebra%' and street_nbr = 333 
      and il.address_id = address.address_id) addr 
where 
    (addr.item_location_id = item.location_a or 
    addr.item_location_id = item.location_b) 

Этот запрос работает очень медленно, так как там нет индексов на Address столе, и я не могу добавить один. Есть ли что-то еще, что я должен был сделать, чтобы улучшить скорость? Прямо сейчас, если я использую таблицу адресов, для возврата результата потребуется около 20 секунд.

+1

[Плохие привычки пинать: использование JOIN-файлов в старом стиле] (http://sqlblog.com/blogs/aaron_bertrand/archive/2009/10/08/bad-habits-to-kick-using-old-style- присоединяется.aspx) - стиль старого стиля * разделенный запятыми список таблиц * был заменен на * правильный * ANSI 'JOIN' синтаксис в ANSI - ** 92 ** SQL Standard (** более 20 лет ** назад) и его использование не рекомендуется –

+0

@marc_s, но я видел дискуссию, где в переполнении стека на том же самом. И производительность не имеет большого значения для более старого способа присоединения к ANSI-соединению. (Ofcoz я буду копать вашу ссылку дальше на том же самом). – Zeus

+0

Сколько строк в каждой таблице, соответственно, сколько строк вы ожидаете получить? –

ответ

-1

Это действительно уродливый запрос. Помимо того факта, что есть ошибки, вы игнорируете множество лучших практик, и хотя это может существенно не улучшить вашу производительность, при написании запросов, которые легко читать, значительно улучшится ваша способность поддерживать запросы в будущем. Прежде всего: если вы используете псевдонимы, вы должны использовать их последовательно, как путем наложения всех ваших таблиц, так и путем префикса всех ваших столбцов. Кроме того, вы должны действительно думать о расстоянии. Просто изменив эти две вещи (и несколько других вопросов), мы получим следующее:

select i.item_id 
from Item i, 
    (select ad.address_id, 
      il.item_location_id 
    from address ad, 
      Item_location il 
    where 
      ad.street_nm like '%zebra%' and ad.street_nbr = 333 
      and il.address_id = ad.address_id) addr 
where 
     addr.item_location_id = i.location_a 
     or addr.item_location_id = i.location_b 

Wow! Это настолько читаемо. Теперь я могу ясно видеть, что вы используете устаревшую версию соединений таблиц, и это должно измениться на явное соединение (стандартизация yay). Кроме того, я могу ясно видеть, что вам не нужен подзапрос для этого запроса. Удаление подзапроса, скорее всего, улучшит производительность вашего запроса (это может быть только немного, но нам нравится любая и вся прибыль). Устанавливаем эти проблемы:

select i.item_id 
from Item i 
    join address ad 
     on (addr.item_location_id = il.location_a 
      or addr.item_location_id = il.location_b) 
    join Item_location il 
     on il.address_id = ad.address_id 
where 
     ad.street_nm like '%zebra%' and ad.street_nbr = 333 

Вдруг это выглядит намного лучше. Теперь мы можем действительно отточить самую большую проблему (по-моему) в вашем запросе.

on (addr.item_location_id = il.location_a 
or addr.item_location_id = il.location_b) 

Забавный факт, потому что вы не выбирали или наложений ничего как location_a и location_b в исходном запросе, он не выполнил бы и вы получили бы ошибку. Я просто собираюсь идти вперед и думаю, что это убивает ваш запрос. Первоначально я не мог видеть этот большой жир or, висящий в качестве условия соединения. Моя рекомендация состоит в том, чтобы разделить это на двух запросов и бросить в союз все:

select i.item_id 
from Item i 
    join address ad 
     on addr.item_location_id = il.location_a 
    join Item_location il 
     on il.address_id = ad.address_id 
where 
     ad.street_nm like '%zebra%' and ad.street_nbr = 333 

union all 

select i.item_id 
from Item i 
    join address ad 
     on addr.item_location_id = il.location_b 
    join Item_location il 
     on il.address_id = ad.address_id 
where 
     ad.street_nm like '%zebra%' and ad.street_nbr = 333 

Без индексации, я думаю, что этот запрос будет вашим лучшим выбором с точки зрения производительности. Даже если это не ускорит работу слишком много, посмотрите, насколько красивее! Будущие разработчики вашей организации будут вам благодарны.

+1

ваш запрос на самом деле выглядит менее читаемым и меняется «или» на «объединение всех» не имеет ничего общего с лучшей производительностью - вы сканируете одну и ту же таблицу дважды. –

+0

@beherenow Я думаю, что или в состоянии соединения, возможно, было бы больно, но вы можете быть прав насчет дополнительного сканирования, вызывающего больше проблем. Если бы я был OP, я бы, наверное, попробовал несколько вещей и посмотрел, сколько времени на самом деле выполняются различные запросы. Обратите внимание на запрос перед редактированием marc. Я бы сказал, что дополнительный интервал абсолютно улучшает читаемость. – Jenn

+0

@beherenow: Это зависит от двигателя. Во многих случаях, когда есть соединение 'или ', база данных будет решать дважды сканировать таблицу. – Allan

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