Hàm Row_Number () trong mệnh đề Where


90

Tôi tìm thấy một câu hỏi được trả lời với Row_Number()chức năng trong mệnh đề where. Khi tôi thử một truy vấn, tôi gặp lỗi sau:

"Msg 4108, Mức 15, Trạng thái 1, Dòng 1 Các chức năng có cửa sổ chỉ có thể xuất hiện trong mệnh đề SELECT hoặc ORDER BY."

Đây là truy vấn tôi đã thử. Nếu ai đó biết cách giải quyết vấn đề này, vui lòng cho tôi biết.

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) > 0sẽ luôn đánh giá đếnTRUE
Quassnoi 23/09/09

3
Đúng vậy, đúng vậy. Tôi không lo lắng về tình trạng bệnh, mà tôi có thể thay đổi bất cứ lúc nào. Tôi muốn truy vấn hoạt động trước, sau đó nghĩ đến việc giữ thợ cắt dây trong khoảng 500 đến 800 ... cảm ơn

2
@Joseph: Tại sao bạn cố tránh sử dụng CTE?
OMG Ponies

1
@rexem - Tôi không phải là chuyên gia về SQL Server. Tôi đang cố gắng giúp đỡ một nhóm trong một dự án lớn mà họ đang phải đối mặt với rất nhiều vấn đề về hiệu suất. Họ đang sử dụng UDF và CTE. Trong một bảng, họ chỉ có 5000 bản ghi và nếu 5 người dùng truy cập vào một tìm kiếm, thì phải mất hơn một phút để truy xuất. Một thời gian, nó không thành công và hết thời gian. Vì vậy, tôi đang cố gắng tránh CTE và UDF và cố gắng đưa ra một truy vấn SQL chuyển tiếp có thể giải quyết các vấn đề về hiệu suất.

1
Xin chào tất cả, Vui lòng xem liên kết tôi đã đăng bên dưới có câu trả lời bằng cách sử dụng row_number () theo một cách khác. Ai đó có thể so sánh truy vấn ban đầu của tôi với truy vấn trong liên kết không? Đánh giá cao sự giúp đỡ ..

Câu trả lời:


91

Để giải quyết vấn đề này, hãy bọc câu lệnh select của bạn trong một CTE, sau đó bạn có thể truy vấn CTE và sử dụng kết quả của hàm cửa sổ trong mệnh đề 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
Tôi đang cố gắng tránh CTE. Đó là trường hợp tồi tệ hơn tôi đang tìm kiếm. cảm ơn

3
Nó có thể chạy nhanh hơn nếu bạn sử dụng truy vấn con thay vì CTE. Tôi đã nhìn thấy hiệu suất tốt hơn bởi một yếu tố của 1,5 trong một số trường hợp
Brian Webster

3
Nên có cũng TOP trong CTE CHỌN khác SQL Server 2008 sẽ không thực hiện truy vấn vì ORDER BY (mà không được hỗ trợ trừ khi TOP được sử dụng)
Muflix

2
Tôi đang sử dụng SQL2005 (ugh) - Tôi có thể tránh việc sử dụng "TOP", bằng cách bỏ "ORDER BY" sau FROM. Nó là dư thừa với (Order By) sau OVER.
Joe B

Tôi đang ước có một cách để sử dụng ROW_NUMBER()trong WHEREmệnh đề mà không CTE :(
Jalal

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

Lưu ý rằng bộ lọc này là dư thừa: ROW_NUMBER()bắt đầu từ 1và luôn lớn hơn 0.


2
@ DavideChicco.it: trong SQL Server, các bảng dẫn xuất yêu cầu một bí danh (tôi nên viết AS qthay thế, nhưng điều này cũng sẽ hoạt động).
Quassnoi

2
Khả năng đọc là một trọng tâm tôi đặt ra khi đặt tên cho bí danh. Bạn có thể viết rn là RowNumber và q là DerivedTable và mệnh đề where là where DerivedTable.RowNumber> 0. Theo ý kiến ​​của tôi, điều này sẽ ít gây nhầm lẫn hơn trong thời gian 6 tháng khi mã không còn mới trong tâm trí bạn.
Edward Comeau

2
@EdwardComeau: rnlà từ viết tắt được chấp nhận rộng rãi cho số hàng ngày nay. Hãy thử nhập "row_number over as ..." vào chuỗi tìm kiếm của google và xem nó gợi ý cho bạn điều gì.
Quassnoi

3
@Quassnoi, khả năng đọc là chìa khóa để mã hóa tốt và nỗ lực nhận thức của việc dịch rn (hoặc các bí danh viết tắt khác) sẽ giúp ích cho chính bạn và những người duy trì mã của bạn. NB, Lần truy cập đầu tiên của Microsoft, CHỌN ROW_NUMBER () HẾT (ĐẶT HÀNG THEO SalesYTD DESC) NHƯ Hàng, ... Tôi cũng chưa xem qua rn trước đó nên số dặm của bạn trong "phổ thông" có thể khác nhau.
Edward Comeau

1
@Quassnoi và lần truy cập thứ hai, bài viết SO - stackoverflow.com/questions/961007/how-do-i-use-row-number một số biến thể chứ không phải rn ;-)
Edward Comeau

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

Tôi nghĩ bạn muốn một cái gì đó như thế này:

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

4
Tạo bí danh cho bảng nếu truy vấn trên không phù hợp với bạn. Sửa đổi dòng cuối cùng thứ hai vì From V_EMPLOYEE) Ađó là thêm A làm bí danh.
Hammad Khan

7

Để đáp lại các nhận xét về câu trả lời của rexem, về việc liệu một chế độ xem nội tuyến hay CTE sẽ nhanh hơn, tôi đọc lại các truy vấn để sử dụng một bảng mà tôi và mọi người có sẵn: 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

Các kế hoạch truy vấn được tạo ra hoàn toàn giống nhau. Tôi mong đợi trong mọi trường hợp, trình tối ưu hóa truy vấn sẽ đưa ra cùng một kế hoạch, ít nhất là thay thế đơn giản CTE bằng chế độ xem nội tuyến hoặc ngược lại.

Tất nhiên, hãy thử các truy vấn của riêng bạn trên hệ thống của riêng bạn để xem liệu có sự khác biệt hay không.

Ngoài ra, row_number()trong mệnh đề where là một lỗi phổ biến trong các câu trả lời được đưa ra trên Stack Overflow. Tính logic row_number()không có sẵn cho đến khi mệnh đề select được xử lý. Mọi người quên điều đó và khi họ trả lời mà không kiểm tra câu trả lời, câu trả lời đôi khi sai. (Bản thân tôi đã bị buộc tội.)


1
Thx Shannon. Bạn đang sử dụng phiên bản SQL Server nào?
OMG Ponies

1
Vậy có nghĩa là, câu trả lời được cung cấp trong liên kết đó là sai? Nhưng, người đăng câu hỏi đã đồng ý rằng nó đang hoạt động .. Đáng ngạc nhiên .. :-)

2
@Joseph, nhưng nếu bạn nhìn vào một câu trả lời khác được đăng bởi OP trong câu hỏi được liên kết, bạn sẽ thấy rằng anh ta liên kết đến một phiên bản của mã không giống với câu trả lời được chấp nhận. Tôi không biết tại sao anh ấy chấp nhận câu trả lời, mặc dù nó sẽ không chạy như đã nhập. Có thể nó đã được chỉnh sửa vào một thời điểm nào đó sau khi được chấp nhận, có thể là đủ để khiến anh ấy tiếp tục, ngay cả khi không hoàn toàn chính xác.
Shannon thôi việc vào

1
@Rexem: Cả SQL Server 2005 và SQL Server 2008. Các phiên bản trước đó không hỗ trợ CTE hoặc ROW_NUMBER ()
Shannon Từ chức

6

Tôi cảm thấy như tất cả các câu trả lời cho thấy việc sử dụng CTE hoặc Sub Query là đủ để sửa lỗi này, nhưng tôi không thấy ai hiểu được lý do tại sao OP gặp sự cố. Lý do tại sao những gì OP đề xuất không hoạt động là do thứ tự xử lý truy vấn logic ở đây:

  1. TỪ
  2. TRÊN
  3. THAM GIA
  4. Ở ĐÂU
  5. NHÓM THEO
  6. VỚI CUBE / ROLLUP
  7. ĐANG CÓ
  8. LỰA CHỌN
  9. DISTINCT
  10. ĐẶT BỞI
  11. HÀNG ĐẦU
  12. OFFSET / FETCH

Tôi tin rằng điều này góp phần rất lớn vào câu trả lời, bởi vì nó giải thích tại sao những vấn đề như thế này lại xảy ra. WHEREluôn được xử lý trước khi SELECTthực hiện một CTE hoặc Truy vấn phụ cần thiết cho nhiều chức năng. Bạn sẽ thấy điều này rất nhiều trong SQL Server.


4

Sử dụng 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

Sử dụng chế độ xem Nội tuyến / Thay thế Tương đương Không phải CTE:

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
Cái nào tốt hơn về hiệu suất? Sử dụng CTE hay truy vấn con? cảm ơn

1
Hãy xem câu trả lời của Shannon - trong bài kiểm tra của anh ấy, chúng bằng nhau.
OMG Ponies

6
Không, nó không nhanh hơn. Trong SQL Server, CTE's và quan điểm inline là những điều tương tự và có cùng hiệu suất. Khi các hàm không xác định được sử dụng trong a CTE, nó được đánh giá lại trên mỗi lần gọi. Người ta phải dùng những thủ đoạn bẩn thỉu để cưỡng bức vật chất a CTE. Xem các bài viết trong blog của tôi: explainextended.com/2009/07/28/... explainextended.com/2009/05/28/generating-xml-in-subqueries
Quassnoi

2

dựa trên câu trả lời của OP cho câu hỏi:

Vui lòng xem liên kết này. Nó có một giải pháp khác, có vẻ hiệu quả với người đặt câu hỏi. Tôi đang cố gắng tìm ra một giải pháp như thế này.

Truy vấn phân trang sử dụng sắp xếp trên các cột khác nhau bằng ROW_NUMBER () OVER () trong SQL Server 2005

~ Joseph

"phương pháp 1" giống như truy vấn của OP từ câu hỏi được liên kết và "phương pháp 2" giống như truy vấn từ câu trả lời đã chọn. Bạn phải xem mã được liên kết trong câu trả lời này để xem điều gì đang thực sự xảy ra, vì mã trong câu trả lời đã chọn đã được sửa đổi để làm cho nó hoạt động. Thử cái này:

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

ĐẦU RA:

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, khi sử dụng SET SHOWPLAN_ALL ON phương pháp 1 có TotalSubtreeCost là 0,08424953, trong khi phương pháp 2 là 0,02627153. phương pháp 2 tốt hơn gấp ba lần.
KM.

1
@rexem, cả phương pháp 1 và 2 đều sử dụng CTE, cách chúng phân trang và sắp xếp hàng là khác nhau. Tôi không chắc tại sao câu hỏi thực tế này lại khác với câu hỏi mà OP liên kết đến (trong câu trả lời cho câu hỏi này của OP), nhưng câu trả lời của tôi tạo mã hoạt động dựa trên liên kết mà OP đề cập đến
KM.

1
Cảm ơn, tôi đang cố gắng so sánh bài đăng cũ và câu trả lời này. [Tôi không biết cách định dạng cái này] Đây là câu trả lời do Tomalak cung cấp. stackoverflow.com/questions/230058?sort=votes#sort-top Điều này có sai không? Nếu anh ấy chỉ đăng một nửa câu trả lời, tôi sẽ tiếp tục với cách hiệu suất tốt hơn của anh ấy để thực hiện truy vấn của mình như thế nào? Xin hãy cho tôi thêm chút ánh sáng để tiếp tục .. cảm ơn

@Joseph, câu trả lời đã chọn trong liên kết mà bạn cung cấp ( stackoverflow.com/questions/230058?sort=votes#sort-top ) khác với mã làm việc mà người đặt câu hỏi cung cấp như hoạt động trong câu trả lời của họ: stackoverflow.com/ câu hỏi / 230058 /… nếu bạn đọc câu trả lời đó, bạn sẽ thấy liên kết đến mã của họ: pastebin.com/f26a4b403 và liên kết đến phiên bản Tomalak của họ: pastebin.com/f4db89a8e trong câu trả lời của tôi, tôi cung cấp phiên bản hoạt động của từng phiên bản bằng cách sử dụng bảng biến
KM.

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
Chào mừng bạn đến với Stack Overflow! Mặc dù đoạn mã này có thể là giải pháp, nhưng bao gồm phần giải thích thực sự giúp cải thiện chất lượng bài đăng của bạn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho người đọc trong tương lai và những người đó có thể không biết lý do cho đề xuất mã của bạn.
Johan

Vui lòng thêm một số ngữ cảnh vào đoạn mã vì lợi ích của người đọc trong tương lai.
DebanjanB
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.