Функція SQL Row_Number () у пункті Where


90

Я знайшов одне запитання, на яке відповідає Row_Number()функція у реченні where. Коли я спробував один запит, я отримав таку помилку:

"Повідомлення 4108, рівень 15, стан 1, рядок 1, віконні функції можуть відображатися лише в реченнях SELECT або ORDER BY."

Ось запит, який я спробував. Якщо хтось знає, як це вирішити, дайте мені знати.

SELECT employee_id 
FROM V_EMPLOYEE 
WHERE row_number() OVER ( ORDER BY employee_id ) > 0 
ORDER BY Employee_ID

9
ROW_NUMBER() OVER (ORDER BY employee_id) > 0завжди буде оцінювати доTRUE
Квасной

3
Так, це правильно. Мене не турбує стан, який я можу змінити будь-коли. Я хочу, щоб запит спершу спрацював, а потім думав про збереження числа від 500 до 800 ... дякую

2
@ Джозеф: Чому ви намагаєтеся уникати використання CTE?
OMG Ponies

1
@rexem - Я не фахівець у SQL Server. Я намагаюся допомогти команді у великому проекті, де вони стикаються з багатьма проблемами з продуктивністю. Вони використовують UDF та CTE. В одній з таблиць вони містять лише 5000 записів, і якщо 5 користувачів отримують доступ до пошуку, то для отримання потрібно більше хвилини. Деякий час це не вдається і час очікування закінчується. Отже, я намагаюся уникати CTE та UDF і намагаюся придумати прямий запит SQL, який може вирішити проблеми з продуктивністю.

1
Привіт усім! Будь ласка, перегляньте посилання, яке я розмістив нижче, яке відповідає іншим способом, використовуючи row_number (). Хтось може порівняти мій початковий запит із запитом у посиланні? Цінуйте допомогу ..

Відповіді:


91

Щоб обійти цю проблему, оберніть ваш оператор select у CTE, а потім ви зможете здійснити запит щодо CTE і використовувати результати віконної функції в реченні where.

WITH MyCte AS 
(
    select   employee_id,
             RowNum = row_number() OVER ( order by employee_id )
    from     V_EMPLOYEE 
    ORDER BY Employee_ID
)
SELECT  employee_id
FROM    MyCte
WHERE   RowNum > 0

7
Я намагаюся уникати CTE. Це гірший випадок, який я шукаю. подяка

3
Це може працювати швидше, якщо ви використовуєте підзапит замість CTE. У деяких випадках я бачив кращі показники в 1,5 рази
Брайан Вебстер

3
У CTE SELECT також має бути TOP, інакше SQL 2008 Server не буде виконувати запит через ORDER BY (який не підтримується, якщо не використовується TOP)
Muflix

2
Я використовую SQL2005 (тьфу) - я можу уникнути використання "TOP", скинувши "ORDER BY" після FROM. Це надлишкове значення з (Order By) після OVER у будь-якому випадку.
Джо Б

Я пошкодував , що є спосіб використовувати ROW_NUMBER()в WHEREреченні без КТРА :(
Джалал

61
SELECT  employee_id
FROM    (
        SELECT  employee_id, ROW_NUMBER() OVER (ORDER BY employee_id) AS rn
        FROM    V_EMPLOYEE
        ) q
WHERE   rn > 0
ORDER BY
        Employee_ID

Зверніть увагу, що цей фільтр зайвий: ROW_NUMBER()починається з 1і завжди більший за 0.


2
@ DavideChicco.it: у SQL Server похідні таблиці вимагають псевдонім ( AS qзамість цього я мав би писати , але це теж спрацювало б).
Quassnoi 02

2
Читаність - це основна увага, коли я називаю псевдоніми. Ви можете написати rn як RowNumber, а q як DerivedTable, а речення where - де DerivedTable.RowNumber> 0. На мою думку, це стане набагато менш заплутаним через 6 місяців, коли код не свіжий у вашій свідомості.
Едвард Комо

2
@EdwardComeau: на сьогодні rnє загальновизнаною абревіатурою номер рядка. Спробуйте ввести "рядок_номери над як ..." у рядок пошуку Google і подивіться, що це вам пропонує.
Quassnoi

3
@Quassnoi, читабельність є ключем до хорошого кодування, а когнітивні зусилля при перекладі rn (або інших скорочених псевдонімів) складаються як для вас, так і для людей, що підтримують ваш код. Зверніть увагу, Microsoft вперше потрапив, ВИБЕРІТЬ РЯДОК НОМЕР () НАД (ЗАМОВИТИ ПО SalesYTD DESC) ЯК Рядок, ... Я також раніше не стикався з rn, тому ваш пробіг в "універсальному" може змінюватися.
Едвард Комо

1
@Quassnoi, і друге потрапляння, така стаття - stackoverflow.com/questions/961007/how-do-i-use-row-number кілька варіацій, а не rn ;-)
Едвард Комо

32
Select * from 
(
    Select ROW_NUMBER() OVER ( order by Id) as 'Row_Number', * 
    from tbl_Contact_Us
) as tbl
Where tbl.Row_Number = 5

19

Я думаю, ви хочете щось подібне:

SELECT employee_id 
FROM  (SELECT employee_id, row_number() 
       OVER (order by employee_id) AS 'rownumber' 
       FROM V_EMPLOYEE) TableExpressionsMustHaveAnAliasForDumbReasons
WHERE rownumber > 0

4
Створіть псевдонім для таблиці, якщо наведений вище запит не працює для вас. Змініть другий останній рядок, From V_EMPLOYEE) Aтобто додайте A як псевдонім.
Хаммад-хан

7

У відповідь на коментарі щодо відповіді rexem щодо того, чи швидше буде вбудований вигляд чи CTE, я переробив запити, використовуючи таблицю, яку я, і всі, мав: sys.objects.

WITH object_rows AS (
    SELECT object_id, 
        ROW_NUMBER() OVER ( ORDER BY object_id) RN
    FROM sys.objects)
SELECT object_id
FROM object_rows
WHERE RN > 1

SELECT object_id
FROM (SELECT object_id, 
        ROW_NUMBER() OVER ( ORDER BY object_id) RN
    FROM sys.objects) T
WHERE RN > 1

Вироблені плани запитів були абсолютно однаковими. Я би очікував, що в усіх випадках оптимізатор запитів запропонує однаковий план, принаймні, шляхом простої заміни CTE на вбудований вигляд або навпаки.

Звичайно, спробуйте власні запити у власній системі, щоб побачити, чи є різниця.

Крім того, row_number()у реченні where є типовою помилкою у відповідях, наданих у Stack Overflow. Логіка row_number()недоступна, поки не буде оброблено пропозицію select. Люди забувають про це, і коли вони відповідають, не перевіривши відповідь, відповідь часом буває неправильною. (Звинувачення, в якому я сам винен.)


1
Thx Шеннон. Яку версію SQL Server ви використовували?
OMG Ponies

1
То це означає, що відповідь, надана за цим посиланням, є неправильною? Але той, хто розмістив запитання, погодився, що воно працює. Дивно .. :-)

2
@Joseph, але якщо ви подивитесь на іншу відповідь, опубліковану OP у зв’язаному питанні, ви побачите, що він посилається на версію коду, яка не збігається з прийнятою відповіддю. Не знаю, чому він прийняв відповідь, хоча вона й не проходила б так, як була введена. Можливо, це було відредаговано в якийсь момент після прийняття, можливо, цього було достатньо, щоб змусити його піти, навіть не будучи абсолютно коректним.
Шеннон Северанс,

1
@Rexem: І SQL Server 2005, і SQL Server 2008. Раніші версії не підтримують CTE або ROW_NUMBER ()
Shannon Severance

6

Я відчуваю, що всі відповіді, що показують використання CTE або підзапиту, є достатніми виправленнями для цього, але я не бачу, щоб хтось доходив до суті, чому у OP є проблема. Причина, по якій запропонований OP не працює, пов’язана з порядком логічної обробки запитів тут:

  1. ВІД
  2. УВІМК
  3. ПРИЄДНАЙТЕСЬ
  4. ДЕ
  5. ГРУПА ЗА
  6. З КУБОМ / РОЛУПОМ
  7. МАЮЧИ
  8. ВИБЕРІТЬ
  9. ВІДМІНЕННЯ
  10. СОРТУВАТИ ЗА
  11. ТОП
  12. OFFSET / FETCH

Я вважаю, що це суттєво сприяє відповіді, оскільки пояснює, чому виникають такі проблеми. WHEREзавжди обробляється перед тим, як SELECTзробити CTE або допоміжний запит, необхідний для багатьох функцій. Ви побачите це багато в SQL Server.


4

Використання CTE (SQL Server 2005+):

WITH employee_rows AS (
  SELECT t.employee_id,
         ROW_NUMBER() OVER ( ORDER BY t.employee_id ) 'rownum'
    FROM V_EMPLOYEE t)
SELECT er.employee_id
  FROM employee_rows er
 WHERE er.rownum > 1

Використання вбудованого перегляду / альтернативи, що не відповідає КТЕ:

SELECT er.employee_id
  FROM (SELECT t.employee_id,
               ROW_NUMBER() OVER ( ORDER BY t.employee_id ) 'rownum'
          FROM V_EMPLOYEE t) er
 WHERE er.rownum > 1

1
Який з них кращий у виконанні? Використання CTE або підзапиту? подяка

1
Дивіться відповідь Шеннона - у його тесті вони рівні.
OMG Ponies

6
Ні, це не швидше. Погляди In SQL Server, in CTEinline - це одне і те ж і мають однакову ефективність. Коли в a використовуються недетерміновані функції CTE, їх переоцінюють при кожному виклику. Потрібно використовувати брудні трюки, щоб змусити матеріалізацію a CTE. Дивіться ці статті в моєму блозі: explainextended.com/2009/07/28 / ... explainextended.com/2009/05/28/generating-xml-in-subqueries
Quassnoi

2

на основі відповіді ОП на запитання:

Перегляньте це посилання. Він має інше рішення, яке, схоже, працює для того, хто задав питання. Я намагаюся знайти таке рішення.

Сторінковий запит із використанням сортування в різних стовпцях за допомогою ROW_NUMBER () OVER () у SQL Server 2005

~ Джозеф

"метод 1" схожий на запит OP із пов'язаного питання, а "метод 2" - як запит на вибрану відповідь. Вам потрібно було поглянути на код, зв’язаний у цій відповіді, щоб побачити, що насправді відбувається, оскільки код у вибраній відповіді був змінений, щоб він працював. Спробуйте це:

DECLARE @YourTable table (RowID int not null primary key identity, Value1 int, Value2 int, value3 int)
SET NOCOUNT ON
INSERT INTO @YourTable VALUES (1,1,1)
INSERT INTO @YourTable VALUES (1,1,2)
INSERT INTO @YourTable VALUES (1,1,3)
INSERT INTO @YourTable VALUES (1,2,1)
INSERT INTO @YourTable VALUES (1,2,2)
INSERT INTO @YourTable VALUES (1,2,3)
INSERT INTO @YourTable VALUES (1,3,1)
INSERT INTO @YourTable VALUES (1,3,2)
INSERT INTO @YourTable VALUES (1,3,3)
INSERT INTO @YourTable VALUES (2,1,1)
INSERT INTO @YourTable VALUES (2,1,2)
INSERT INTO @YourTable VALUES (2,1,3)
INSERT INTO @YourTable VALUES (2,2,1)
INSERT INTO @YourTable VALUES (2,2,2)
INSERT INTO @YourTable VALUES (2,2,3)
INSERT INTO @YourTable VALUES (2,3,1)
INSERT INTO @YourTable VALUES (2,3,2)
INSERT INTO @YourTable VALUES (2,3,3)
INSERT INTO @YourTable VALUES (3,1,1)
INSERT INTO @YourTable VALUES (3,1,2)
INSERT INTO @YourTable VALUES (3,1,3)
INSERT INTO @YourTable VALUES (3,2,1)
INSERT INTO @YourTable VALUES (3,2,2)
INSERT INTO @YourTable VALUES (3,2,3)
INSERT INTO @YourTable VALUES (3,3,1)
INSERT INTO @YourTable VALUES (3,3,2)
INSERT INTO @YourTable VALUES (3,3,3)
SET NOCOUNT OFF

DECLARE @PageNumber     int
DECLARE @PageSize       int
DECLARE @SortBy         int

SET @PageNumber=3
SET @PageSize=5
SET @SortBy=1


--SELECT * FROM @YourTable

--Method 1
;WITH PaginatedYourTable AS (
SELECT
    RowID,Value1,Value2,Value3
        ,CASE @SortBy
             WHEN  1 THEN ROW_NUMBER() OVER (ORDER BY Value1 ASC)
             WHEN  2 THEN ROW_NUMBER() OVER (ORDER BY Value2 ASC)
             WHEN  3 THEN ROW_NUMBER() OVER (ORDER BY Value3 ASC)
             WHEN -1 THEN ROW_NUMBER() OVER (ORDER BY Value1 DESC)
             WHEN -2 THEN ROW_NUMBER() OVER (ORDER BY Value2 DESC)
             WHEN -3 THEN ROW_NUMBER() OVER (ORDER BY Value3 DESC)
         END AS RowNumber
    FROM @YourTable
    --WHERE
)
SELECT
    RowID,Value1,Value2,Value3,RowNumber
        ,@PageNumber AS PageNumber, @PageSize AS PageSize, @SortBy AS SortBy
    FROM PaginatedYourTable
    WHERE RowNumber>=(@PageNumber-1)*@PageSize AND RowNumber<=(@PageNumber*@PageSize)-1
    ORDER BY RowNumber



--------------------------------------------
--Method 2
;WITH PaginatedYourTable AS (
SELECT
    RowID,Value1,Value2,Value3
        ,ROW_NUMBER() OVER
         (
             ORDER BY
                 CASE @SortBy
                     WHEN  1 THEN Value1
                     WHEN  2 THEN Value2
                     WHEN  3 THEN Value3
                 END ASC
                ,CASE @SortBy
                     WHEN -1 THEN Value1
                     WHEN -2 THEN Value2
                     WHEN -3 THEN Value3
                 END DESC
         ) RowNumber
    FROM @YourTable
    --WHERE  more conditions here
)
SELECT
    RowID,Value1,Value2,Value3,RowNumber
        ,@PageNumber AS PageNumber, @PageSize AS PageSize, @SortBy AS SortBy
    FROM PaginatedYourTable
    WHERE 
        RowNumber>=(@PageNumber-1)*@PageSize AND RowNumber<=(@PageNumber*@PageSize)-1
        --AND more conditions here
    ORDER BY
        CASE @SortBy
            WHEN  1 THEN Value1
            WHEN  2 THEN Value2
            WHEN  3 THEN Value3
        END ASC
       ,CASE @SortBy
            WHEN -1 THEN Value1
            WHEN -2 THEN Value2
            WHEN -3 THEN Value3
        END DESC

ВИХІД:

RowID  Value1 Value2 Value3 RowNumber  PageNumber  PageSize    SortBy
------ ------ ------ ------ ---------- ----------- ----------- -----------
10     2      1      1      10         3           5           1
11     2      1      2      11         3           5           1
12     2      1      3      12         3           5           1
13     2      2      1      13         3           5           1
14     2      2      2      14         3           5           1

(5 row(s) affected

RowID  Value1 Value2 Value3 RowNumber  PageNumber  PageSize    SortBy
------ ------ ------ ------ ---------- ----------- ----------- -----------
10     2      1      1      10         3           5           1
11     2      1      2      11         3           5           1
12     2      1      3      12         3           5           1
13     2      2      1      13         3           5           1
14     2      2      2      14         3           5           1

(5 row(s) affected)

1
fyi, при використанні методу SET SHOWPLAN_ALL ON метод TotalSubtreeCost становив 0,08424953, тоді як метод 2 - 0,02627153. метод 2 був утричі кращий.
КМ.

1
@rexem, обидва способи 1 і 2 використовують CTE, спосіб їх розділення сторінок та порядок рядків різний. Я не впевнений, чому це фактичне запитання настільки відрізняється від питання, на яке посилається OP (у відповіді на це питання OP), але моя відповідь створює робочий код на основі посилання, на яке посилається OP
KM.

1
Дякую, я намагаюся порівняти старий пост і відповіді на це. [Я не знаю, як це форматувати] Ось відповідь, яку надав Томалак. stackoverflow.com/questions/230058?sort=votes#sort-top Це неправильно? Якщо він опублікував лише половину відповіді, як я можу продовжувати його кращий спосіб виконання запиту? Будь ласка, дайте мені трохи світла, щоб продовжити .. дякую

@Joseph, вибрана відповідь у посиланні, яке ви надаєте ( stackoverflow.com/questions/230058?sort=votes#sort-top ), відрізняється від робочого коду, який особа, що задає запитання, надає свою відповідь: stackoverflow.com/ questions / 230058 /… якщо ви прочитаєте цю відповідь, ви побачите посилання на їх код: pastebin.com/f26a4b403 та посилання на їх версію Tomalak: pastebin.com/f4db89a8e. У моїй відповіді я надаю робочу версію кожної версії, використовуючи табличні змінні
КМ.

2
WITH MyCte AS 
(
    select 
       employee_id,
       RowNum = row_number() OVER (order by employee_id)
    from V_EMPLOYEE 
)
SELECT  employee_id
FROM    MyCte
WHERE   RowNum > 0
ORDER BY employee_id

-1
 select salary from (
 select  Salary, ROW_NUMBER() over (order by Salary desc) rn from Employee 
 ) t where t.rn = 2

3
Ласкаво просимо до Stack Overflow! Хоча цей фрагмент коду може бути рішенням, включаючи пояснення, він справді допомагає поліпшити якість вашої публікації. Пам’ятайте, що ви будете відповідати на запитання для читачів у майбутньому, і ці люди можуть не знати причин вашої пропозиції коду.
Йохан

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