Производительность SQL: WHERE vs. WHERE (ROW_NUMBER)

Я хочу получить n-й в m-й записи в таблице, какой лучший выбор в 2 ниже решения:

Решение 1:

    SELECT * FROM Table WHERE ID >= n AND ID <= m

Решение 2:

    SELECT * FROM 
                (SELECT *, 
                        ROW_NUMBER() OVER (ORDER BY ID) AS row 
                 FROM Table 
                )a 
    WHERE row >= n AND row <= m

Ответ 1

Второй ответ - ваш лучший выбор. Он учитывает тот факт, что у вас могут быть отверстия в столбце идентификатора. Я бы переписал его как CTE, но вместо подзапроса...

;WITH MyCTE AS
(SELECT  *,  
         ROW_NUMBER() OVER (ORDER BY ID) AS row  
FROM     Table)
SELECT   *
FROM     MyCTE
WHERE    row >= @start 
         AND row <= @end

Ответ 2

Как уже указывалось, запросы возвращают разные результаты и сравнивают яблоки с апельсинами.

Но основной вопрос остается: , который быстрее: ведомый подкачки или ведомый поисковый вызов с помощью набора строк

Подсветка клавиш

Подкачка, управляемая клавиатурой, опирается на запоминание верхних и нижних клавиш последней отображаемой страницы и запрос следующего или предыдущего набора строк на основе верхнего/последнего набора:

Следующая страница:

select top (<pagesize>) ...
from <table>
where key > @last_key_on_current_page
order by key;

Предыдущая страница:

select top (<pagesize>)
from <table>
where key < @first_key_on_current_page
order by key desc;

Этот подход имеет два основных преимущества перед подходом ROW_NUMBER или над эквивалентным LIMIT-подходом MySQL:

  • правильный: в отличие от подхода на основе номера строки он правильно обрабатывает новые записи и удаляет записи. Последняя строка не отображается как первая строка только потому, что строка 23 на странице была удалена тем временем. Также строки не таинственно исчезают между страницами. Эти аномалии являются общими для подхода на основе row_number, но решение на основе набора ключей делает гораздо лучшую работу, чтобы избежать их.
  • быстро: все операции могут быть решены с помощью быстрого позиционирования строк, за которым следует сканирование диапазона в нужном направлении

Однако этот подход сложный для реализации, который трудно понять среднему программисту и не поддерживается инструментами.

Число строк, приводимое в движение

Это общий подход, введенный с помощью запросов Linq:

select ...
from (
  select ..., row_number() over (...) as rn
  from table)
where rn between @firstRow and @lastRow;

(или аналогичный запрос с использованием TOP) Этот подход легко для реализации и поддерживается инструментами (в частности, операторами Linq.Limit и .Take). Но этот подход гарантированно сканирует индекс, чтобы подсчитывать строки. Этот подход работает очень быстро для страницы 1 и постепенно замедляется, так как один идет на более высокие и более высокие номера страниц.

В качестве бонуса с этим решением очень легко изменить порядок сортировки (просто измените предложение OVER).

В целом, учитывая простоту решений на основе ROW_NUMBER(), поддержку, которую они получают от Linq, простота использования произвольных заказов для умеренных наборов данных, адекватные решения на основе ROW_NUMBER. Для больших и очень больших наборов данных ROW_NUMBER() может вызвать серьезные проблемы с производительностью.

Еще одна вещь, которую следует учитывать, заключается в том, что часто возникает определенная модель доступа. Часто первые несколько страниц горячие, а страницы после 10 в основном никогда не просматриваются (например, самые последние сообщения). В этом случае штраф, который возникает с ROW_NUMBER() для посещения нижних страниц (отображаемые страницы, для которых большое количество строк необходимо подсчитать для получения строки исходного результата), может быть проигнорирован.

И наконец, разбиение на страницы клавиш отлично подходит для навигации по словарям, которые ROW_NUMBER() не может легко разместить. Навигация словаря - это где вместо номера страницы пользователи могут перемещаться по определенным якорям, например, буквам алфавита. Типичным примером является контакт с Rolodex как боковая панель, вы нажимаете M, и вы переходите к первому имени клиента, начинающемуся с M.

Ответ 3

Это разные запросы.

Предполагая, что идентификатор является суррогатным ключом, он может иметь пробелы. ROW_NUMBER будет смежным.

Если вы можете гарантировать, что у вас нет пробелов в данных, тогда 1-й, потому что я надеюсь, что он будет проиндексирован. Второй - более "правильный".

Ответ 4

SELECT * FROM Table WHERE ID BETWEEN N AND N

может быть? (непроверенный и я ржавый)