Як підказка працює з ROW_NUMBER у SQL сервері?


13

У мене є Employeeтаблиця з мільйонами записів. У мене є наступний SQL для підключення даних у веб-програмі. Це прекрасно працює. Однак я вважаю проблемою - похідна таблиця tblEmployeeвибирає всі записи в Employeeтаблиці (щоб створити MyRowNumberзначення).

Я думаю, це спричиняє вибір усіх записів у Employeeтаблиці.

Це дійсно так працює? Або SQL Server оптимізований для вибору лише 5 записів із вихідної Employeeтаблиці?

DECLARE @Index INT;
DECLARE @PageSize INT;

SET @Index = 3;
SET @PageSize = 5;

SELECT *  FROM
  (SELECT  ROW_NUMBER() OVER (ORDER BY EmpID asc) as MyRowNumber,*
  FROM Employee) tblEmployee
WHERE MyRowNumber BETWEEN ( ((@Index - 1) * @PageSize )+ 1) AND @Index*@PageSize 

Відповіді:


17

Альтернативою тесту може бути:

;WITH x AS (SELECT EmpID, k = ROW_NUMBER() OVER (ORDER BY EmpID) FROM dbo.Emp)
SELECT e.columns
FROM x INNER JOIN dbo.Emp AS e
ON x.EmpID = e.EmpID
WHERE x.k BETWEEN (((@Index - 1) * @PageSize) + 1) AND @Index * @PageSize
ORDER BY ...;

Так, ви натискаєте таблицю двічі, але в CTE, де ви скануєте всю таблицю, ви лише захоплюєте ключ, а не ВСІ дані. Але ви дійсно повинні подивитися на цю статтю:

http://www.sqlservercentral.com/articles/T-SQL/66030/

І наступна дискусія:

http://www.sqlservercentral.com/Forums/Topic672980-329-1.aspx

У SQL Server 2012, звичайно, ви можете використовувати новий OFFSET/ FETCH NEXTсинтаксис:

;WITH x AS 
(
  SELECT EmpID FROM dbo.Emp
    ORDER BY EmpID
    OFFSET  @PageSize * (@Index - 1) ROWS
    FETCH NEXT @PageSize ROWS ONLY
)
SELECT e.columns
FROM x INNER JOIN dbo.Emp AS e
ON x.EmpID = e.EmpID
ORDER BY ...; 

Слід зазначити, що OFFSET / FETCH NEXT не дає жодних переваг у порівнянні з методом CTE
Акаш,

2
@Akash Ви ретельно це перевірили? Я спостерігав деякі розбіжності в плані, але конкретно нічого не згадував про продуктивність, тому що я не робив жодного обширного тестування. Навіть якщо продуктивність однакова, синтаксис трохи менш громіздкий. Я робив щоденник про це тут: sqlblog.com/blogs/aaron_bertrand/archive/2010/11/10/…
Аарон Бертран

1
Ах, ти маєш рацію, є різниця в продуктивності. Я читав це: blogs.technet.com/b/dataplatforminsider/archive/2011/11/01/… де він згадує про різницю, але щойно побачив канал9.msdn.com/posts/SQL11UPD03-REC-02, де він показує theres велика різниця .. (хоча в звуковій недооцінці різниця в продуктивності)
Акаш

2

Хоча ви, можливо, не знаєте механізму, який стоїть за ним, ви можете перевірити це самостійно, порівнявши ефективність вашого запиту з: виберіть * від співробітника.

Більш новітні версії SQL Server роблять досить хорошу роботу з оптимізації, але це може залежати від кількох факторів.

Те, як виконує вашу функцію ROW_NUMBER, визначатиметься пунктом «Порядок замовлення». У вашому прикладі більшість здогадається, що EmpID є первинним ключем.

Є деякі , де положення , які є настільки складними і / або погано закодовані або індексується, ви можете бути краще просто повертаючи весь набір даних (це рідко і може бути виправлено). Використання BETWEEN має проблеми.

Перш ніж припустити, що було б краще повернути всі рядки до програми та дозволити це зрозуміти, слід попрацювати над оптимізацією запиту. Перевірте кошториси. Запитайте аналізатор запитів. Перевірте деякі варіанти.


2

Я знаю, що питання стосується row_number (), але я хочу додати одну нову особливість сервера sql 2012. На сервері sql 2012 з'явилася нова функція OFFSET Вибрати наступний, і це дуже швидко, ніж row_number (). Я використовував це, і це дає мені хороший результат, сподіваюся, що ви, хлопці, також заповнили такий же досвід.

Я знайшов один приклад на http://blogfornet.com/2013/06/sql-server-2012-offset-use/

що корисно. Сподіваємось, це допоможе і вам для впровадження нових функцій ....


-2

Я не думаю, що це оцінює повернення всіх рядків у початковій таблиці. SQL-сервер оптимізує. Інакше знадобиться величезна кількість часу, щоб вибрати мільйон записів. Зараз я це використовую, і це набагато швидше, ніж вибір усіх рядків. Тож обов’язково не набирає всіх рядів. Однак це повільніше, ніж просто отримання перших п’яти рядків, ймовірно, через час, витрачений на замовлення


-2
DECLARE @PageIndex int;
DECLARE @PageSize int;
SET @PageIndex = 4;
SET @PageSize = 5;
;With ranked AS   --- Or you can make it a view
(
   SELECT ROW_NUMBER() OVER(ORDER BY IdentityId) AS RowNum,  *
   FROM logeventnew
)
SELECT *   --Your fields here
FROM Ranked
WHERE RowNum BETWEEN ((@PageIndex - 1) * @PageSize + 1)
    AND (@PageIndex * @PageSize)
ORDER BY IdentityId

4
Чи можете ви розширити свою відповідь? Питання стосувалося того, як підказка працює внутрішньо на SQL Server - тобто що робить двигун бази даних для виконання запиту. На жаль, станом на сьогодні ваша відповідь не стосується фактичного занепокоєння.
Містер Броунстоун
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.