2009-07-31 2 views
6

У меня есть запрос по линиямИзготовление SQL запросов более эффективным

select b.* from 
(select key, max(val) as val from (somequery) group by key) as a 
inner join 
(somequery) as b 
on a.key = b.key and a.val = b.val 
order by key 

И мне было интересно, если есть очевидный способ (что мне не хватает), чтобы упростить ее (при условии, что somequery может быть довольно длинный).

Любые мысли будут оценены.

+0

некоторые вопросы? Вам нужно будет уточнить это, если вы ожидаете получить реальную помощь. –

+1

@rexem: OP не работает. Очевидно, что он хочет отбросить только строки с max val. – Eric

ответ

2

Существует, но это, конечно, не очевидно:

select 
    * 
from 
    (
    select 
     key, 
     val, 
     col, 
     max(val) over (partition by key) as MaxVal 
    from 
     tableA 
    ) 
where 
    val = MaxVal 

Используя пункт over это отличный способ сделать это, и не требует каких-либо посторонних подзапросов. Все, что он делает, это взять max val за каждый ключ, а затем обернуть этот результирующий набор в подзапрос, где мы можем проверить val на MaxVal, чтобы убедиться, что мы потянем правильную строку.

Гораздо чище и быстрее, чем до трех подзапросов!

+0

Я еще не видел MAX..OVER. Как это работает для составных клавиш? Я пытаюсь использовать свой собственный стол, и я могу использовать agg/join или ROWNUMBER = 1, но не эту технику. – gbn

+0

'max (val) over (partition by key1, key2, key3)' работает как шарм. – Eric

+0

Спасибо за это, Эрик. – 2009-08-03 01:52:12

-1

Я бы выбрал ваши подзапросы во временные таблицы, прежде чем выбирать их. Я думаю, вы увидите значительное повышение производительности.

+1

Не мой нисходящий канал, но ... если вы выберете во временную таблицу, СУБД должна сохранить эти данные и свои метаданные, а затем использовать их один раз. Было бы более эффективным и эффективным, чтобы дать оптимизатору весь запрос - поэтому он может избежать материализации суб-запроса, если это вообще возможно. Теперь, если есть несколько запросов, которые будут использовать один и тот же подзапрос, тогда вы можете увидеть преимущество от явной таблицы temp, хотя было бы неплохо измерить и убедиться. –

+1

@ Джонатан: Я согласен с тем, что в принципе то, что вы говорите, правильно, но мой опыт работы с SQL-сервером предполагает, что для больших таблиц (скажем, ничего большего, чем 2000 строк) этот маршрут намного эффективнее. – Jon

0

Для этого вы хотите использовать ROW_NUMBER() или RANK().

(и, пожалуйста, убедитесь, что предыдущий запрос заканчивается точкой с запятой)

with ranked as 
(
select *, row_number() over (partition by key order by val desc) as bestrow 
from sometableorquery 
) 
select * 
from ranked 
where bestrow = 1 
order by key; 

Если вы хотите связи (так, что ключ, который имеет два лучших значения возвращает оба), а затем использовать знак() вместо row_number().

Rob

+0

Кроме того - если вы хотите получить верхнюю часть 3 каждого ключа, попробуйте «WHERE bestrow <= 3» –

+1

ROW_NUMBER вернет только одну строку за ключ. Если код OP будет связываться в случае наличия более одной строки для ключа с val = max (val). Замена ROW_NUMBER() с помощью RANK() сохранит первоначальное намерение. –

+0

Спасибо Шеннон. Я упомянул о том, «там, где вы хотите иметь дело с галстуками». –