2013-03-07 1 views
3

Этот (измененный для простоты) запрос является частью более крупного запроса и соединен по дате с другими выборами. Однако я привязал этот раздел к собаке медленно. Скажем, у меня есть таблица UserLoginHistory, которая регистрирует каждый логин для пользователя. Для каждого пользователя, я хочу дату их первыми авторизованы. (Позже в запросе, I группы по LogDate, чтобы получить сколько первый раз логинов были каждый день.)Производительность с NOT EXISTS - запрос t-sql

select 
    LogDate, --(this value is only date, no time) 
    UserId 
from 
    UserLoginHistory ul 
where 
    not exists 
     (
      select 
       * 
      from 
       UserLoginHistory ulPrevious 
      where 
       ulPrevious.LogDate < ul.LogDate 
       and ul.UserId = ulPrevious.UserId 
     ) 
group by ul.LogDate, ul.UserId 

Очевидно, что НЕ СУЩЕСТВУЕТ-часть является медленным. Но я не могу понять, как заменить его чем-то более эффективным, выполняя ту же работу.

С небольшим счетом UserLogHistory производительность не составит труда. Когда я добираюсь до 15 000, он начинает замедляться. Может быть, я должен выставить результат за каждый день в другую таблицу, но я бы хотел найти лучшее решение этого вопроса, так как там должно быть одно ...

Спасибо за ваше время!

+0

По определению NOT EXISTS должны выполнять сканирование таблицы. Ключом к настройке его производительности является сделать отношение, которое необходимо отсканировать как можно меньше, в идеале - некластеризованный индекс. Не зная, какие индексы существуют на вашем столе, невозможно дать более конкретные советы. –

+3

В большинстве случаев это лучший метод между 'not in' и' left external join, где ключ имеет значение null: [Aaron Bertrand проверил его] (http://www.sqlperformance.com/2012/12/t-sql -queries/левый анти-полусоединение). –

+2

Вопросы производительности, как правило, очень специфичны для платформы, в какой базе данных вы используете: SQL Server или Sybase? И какие показатели у вас есть на столе? 15 000 строк не очень много, поэтому кажется, что ваша индексация может оказаться не оптимальной. – Pondlife

ответ

4

Вы можете использовать метод нумерации строк:

select LogDate,UserId from (
    select 
     LogDate, 
     UserId 
     row_number() over (partition by UserId order by LogDate) as rown 
    from 
     UserLoginHistory ul 
) 
where rown = 1 

строк для каждого идентификатора нумеруются LogDate, поэтому ранний один всегда будет номером 1.

Примечание: Я не думаю, group by в вашем исходном запросе был необходим - статья not exists должна гарантировать, что вы получите уникальные комбинации UserId и LogDate.

+0

Теперь, когда мир изменился. – cederlof

4

Если вас интересуют только те 2 поля, не можете ли вы использовать простой агрегат?

+0

Спасибо, что, вероятно, будет работать для простого сценария, но я выбрал ответ dan1111, поскольку он был бы более гибким для меня. – cederlof