SQL Row_Number () berfungsi di Where Clause

91

Saya menemukan satu pertanyaan terjawab dengan Row_Number()fungsi di klausa where. Ketika saya mencoba satu kueri, saya mendapatkan kesalahan berikut:

"Msg 4108, Level 15, State 1, Line 1 Windowed functions hanya dapat muncul di klausa SELECT atau ORDER BY."

Inilah pertanyaan yang saya coba. Jika ada yang tahu cara menyelesaikan ini, beri tahu saya.

SELECT employee_id 
FROM V_EMPLOYEE 
WHERE row_number() OVER ( ORDER BY employee_id ) > 0 
ORDER BY Employee_ID
johnnyRose
sumber
10
ROW_NUMBER() OVER (ORDER BY employee_id) > 0akan selalu mengevaluasi keTRUE
Quassnoi
3
Ya, benar. Saya tidak khawatir dengan kondisi yang bisa saya ubah setiap saat. Saya ingin kueri berfungsi terlebih dahulu, lalu berpikir untuk mempertahankan nomor baris antara 500 dan 800 ... terima kasih
2
@ Joseph: Mengapa Anda mencoba menghindari penggunaan CTE?
OMG Ponies
1
@ rexem - Saya bukan ahli dalam SQL Server. Saya mencoba membantu tim dalam proyek besar di mana mereka menghadapi banyak masalah dengan kinerja. Mereka menggunakan UDF dan CTE. Di salah satu tabel, mereka hanya memiliki 5000 record, dan jika 5 pengguna mengakses pencarian, butuh lebih dari satu menit untuk mengambilnya. Suatu saat, gagal dan waktu habis. Jadi, saya mencoba menghindari CTE dan UDF dan mencoba membuat kueri SQL langsung yang dapat memecahkan masalah kinerja.
1
Halo semuanya, Silakan lihat tautan yang saya posting di bawah ini yang jawabannya menggunakan row_number () dengan cara yang berbeda. Bisakah seseorang membandingkan kueri awal saya dengan yang ada di tautan? Menghargai bantuan ..

Jawaban:

95

Untuk mengatasi masalah ini, bungkus pernyataan pemilihan Anda dalam CTE, dan kemudian Anda dapat melakukan kueri terhadap CTE dan menggunakan hasil fungsi berjendela di klausa 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
Scott Ivey
sumber
7
Saya mencoba menghindari CTE. Itulah kasus terburuk yang saya cari. terima kasih
3
Ini mungkin berjalan lebih cepat jika Anda menggunakan subquery, bukan CTE. Saya telah melihat kinerja yang lebih baik dengan faktor 1,5 dalam beberapa kasus
Brian Webster
3
Harus ada juga TOP di CTE SELECT jika tidak SQL 2008 Server tidak akan mengeksekusi kueri karena ORDER BY (yang tidak didukung kecuali TOP digunakan)
Muflix
2
Saya menggunakan SQL2005 (ugh) - Saya dapat menghindari penggunaan "TOP", dengan menghapus "ORDER BY" setelah FROM. Itu berlebihan dengan (Order By) setelah OVER bagaimanapun juga.
Joe B
Saya berharap ada cara untuk menggunakan ROW_NUMBER()di WHEREklausul tanpa CTE :(
Jalal
62
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

Perhatikan bahwa filter ini berlebihan: ROW_NUMBER()dimulai dari 1dan selalu lebih besar dari 0.

Quassnoi
sumber
2
@ DavideChicco.it: di SQL Server, tabel turunan memerlukan alias (saya seharusnya menulis AS qsebagai gantinya, tetapi ini juga akan berhasil).
Quassnoi
2
Keterbacaan adalah fokus yang saya miliki saat memberi nama alias. Anda dapat menulis rn sebagai RowNumber dan q sebagai DerivedTable dan klausa where sebagai where DerivedTable.RowNumber> 0. Menurut pendapat saya, ini akan jauh lebih membingungkan dalam waktu 6 bulan ketika kode tidak segar dalam pikiran Anda.
Edward Comeau
2
@EdwardComeau: rnadalah akronim yang diterima secara universal untuk nomor baris saat ini. Coba ketikkan "row_number over as ..." ke dalam string pencarian google dan lihat apa yang disarankannya untuk Anda.
Quassnoi
3
@Quassnoi, keterbacaan adalah kunci untuk pengkodean yang baik dan upaya kognitif menerjemahkan rn (atau alias yang disingkat lainnya) bertambah untuk Anda sendiri dan orang-orang yang memelihara kode Anda. NB, Microsoft first hit, SELECT ROW_NUMBER () OVER (ORDER BY SalesYTD DESC) AS Row, ... Saya juga belum menemukan rn sebelumnya sehingga jarak tempuh Anda dalam "universal" mungkin berbeda.
Edward Comeau
1
@Quassnoi, dan hit kedua, artikel SO - stackoverflow.com/questions/961007/how-do-i-use-row-number beberapa variasi dan bukan 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
swa
sumber
19

Saya pikir Anda menginginkan sesuatu seperti ini:

SELECT employee_id 
FROM  (SELECT employee_id, row_number() 
       OVER (order by employee_id) AS 'rownumber' 
       FROM V_EMPLOYEE) TableExpressionsMustHaveAnAliasForDumbReasons
WHERE rownumber > 0
Matthew Jones
sumber
4
Buat alias untuk tabel jika kueri di atas tidak berhasil untuk Anda. Ubah baris terakhir kedua karena From V_EMPLOYEE) Amenambahkan A sebagai alias.
Hammad Khan
7

Menanggapi komentar pada jawaban rexem, sehubungan dengan apakah tampilan inline atau CTE akan lebih cepat, saya menyusun ulang kueri untuk menggunakan tabel I, dan semua orang, telah tersedia: 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

Rencana kueri yang dihasilkan persis sama. Saya berharap dalam semua kasus, pengoptimal kueri akan menghasilkan rencana yang sama, setidaknya dalam penggantian sederhana CTE dengan tampilan sebaris atau sebaliknya.

Tentu saja, coba kueri Anda sendiri di sistem Anda sendiri untuk melihat apakah ada perbedaan.

Juga, row_number()di mana klausa adalah kesalahan umum dalam jawaban yang diberikan di Stack Overflow. Logicaly row_number()tidak tersedia sampai klausa pemilihan diproses. Orang-orang melupakannya dan ketika mereka menjawab tanpa menguji jawabannya, terkadang jawabannya salah. (Tuduhan yang membuat saya sendiri bersalah.)

Shannon Severance
sumber
1
Terima kasih Shannon. Versi SQL Server apa yang Anda gunakan?
OMG Ponies
1
Artinya, jawaban yang diberikan di tautan itu salah? Tapi, orang yang memposting pertanyaan setuju bahwa itu berhasil .. Mengejutkan .. :-)
2
@Joseph, tetapi jika Anda melihat jawaban lain yang diposting oleh OP di pertanyaan terkait, Anda akan melihat bahwa dia menautkan ke versi kode yang tidak sama dengan jawaban yang diterima. Saya tidak tahu mengapa dia menerima jawabannya, meskipun itu tidak akan berjalan saat masuk. Mungkin itu diedit di beberapa titik setelah diterima, mungkin itu cukup untuk membuatnya pergi, bahkan tanpa sepenuhnya benar.
Shannon Severance
1
@Rexem: SQL Server 2005 & SQL Server 2008. Versi sebelumnya tidak mendukung CTE atau ROW_NUMBER ()
Shannon Severance
6

Saya merasa semua jawaban yang menunjukkan penggunaan CTE atau Sub Query adalah perbaikan yang cukup untuk ini, tetapi saya tidak melihat ada yang sampai pada inti mengapa OP memiliki masalah. Alasan mengapa apa yang disarankan OP tidak berfungsi adalah karena urutan pemrosesan kueri logis di sini:

  1. DARI
  2. DI
  3. IKUTI
  4. DIMANA
  5. GRUP OLEH
  6. DENGAN CUBE / ROLLUP
  7. HAVING
  8. PILIH
  9. BERBEDA
  10. DIPESAN OLEH
  11. PUNCAK
  12. OFFSET / FETCH

Saya yakin ini berkontribusi besar pada jawaban, karena menjelaskan mengapa masalah seperti ini terjadi. WHEREselalu diproses sebelum SELECTmembuat CTE atau Sub Query yang diperlukan untuk banyak fungsi. Anda akan sering melihat ini di SQL Server.

Jamie Marshall
sumber
4

Menggunakan 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

Menggunakan Tampilan Inline / Alternatif Setara Non-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
OMG Ponies
sumber
1
Mana yang lebih baik dalam performa? Menggunakan CTE atau subquery? terima kasih
1
Lihat jawaban Shannon - dalam ujiannya mereka setara.
OMG Ponies
6
Tidak, ini tidak lebih cepat. Dalam SQL Server, CTEtampilan dan inline adalah hal yang sama dan memiliki kinerja yang sama. Ketika fungsi non-deterministik digunakan dalam a CTE, fungsi tersebut dievaluasi ulang pada setiap panggilan. Seseorang harus menggunakan trik kotor untuk memaksa perwujudan a CTE. Lihat artikel ini di blog saya: menjelaskanextended.com/2009/07/28/… menjelaskanextended.com/2009/05/28/generating-xml-in-subqueries
Quassnoi
2

berdasarkan jawaban OP atas pertanyaan:

Silakan lihat tautan ini. Ini memiliki solusi yang berbeda, yang terlihat bekerja untuk orang yang mengajukan pertanyaan. Saya mencoba mencari solusi seperti ini.

Kueri paginasi menggunakan pengurutan pada kolom berbeda menggunakan ROW_NUMBER () OVER () di SQL Server 2005

~ Joseph

"metode 1" seperti kueri OP dari pertanyaan terkait, dan "metode 2" seperti kueri dari jawaban yang dipilih. Anda harus melihat kode yang ditautkan dalam jawaban ini untuk mengetahui apa yang sebenarnya terjadi, karena kode dalam jawaban yang dipilih telah dimodifikasi untuk membuatnya berfungsi. Coba ini:

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

KELUARAN:

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)
KM.
sumber
1
Perlu diketahui , saat menggunakan SET SHOWPLAN_ALL ON metode 1 memiliki TotalSubtreeCost 0.08424953, sedangkan metode 2 berada di 0.02627153. metode 2 lebih dari tiga kali lebih baik.
KM.
1
@ rexem, kedua metode 1 dan 2 menggunakan CTE, cara mereka membuat halaman dan mengurutkan baris berbeda. Saya tidak yakin mengapa pertanyaan sebenarnya ini sangat berbeda dari pertanyaan yang ditautkan oleh OP (dalam jawaban atas pertanyaan ini oleh OP), tetapi jawaban saya membuat kode yang berfungsi berdasarkan tautan yang dirujuk OP
KM.
1
Terima kasih, saya mencoba membandingkan posting lama dan jawaban ini. [Saya tidak tahu bagaimana memformat ini] Berikut adalah jawaban yang diberikan oleh Tomalak. stackoverflow.com/questions/230058?sort=votes#sort-top Apakah ini salah? Jika dia hanya memposting setengah dari jawaban, bagaimana saya akan melanjutkan cara kinerjanya yang lebih baik dalam melakukan kueri saya? Tolong beri saya sedikit lebih banyak cahaya untuk melanjutkan .. terima kasih
@Joseph, jawaban yang dipilih di tautan yang Anda berikan ( stackoverflow.com/questions/230058?sort=votes#sort-top ) berbeda dari kode kerja yang diberikan oleh orang yang mengajukan pertanyaan sebagai yang berfungsi dalam jawaban mereka: stackoverflow.com/ pertanyaan / 230058 /… jika Anda membaca jawaban itu, Anda akan melihat tautan ke kode mereka: pastebin.com/f26a4b403 dan tautan ke versi Tomalak mereka: pastebin.com/f4db89a8e dalam jawaban saya Saya menyediakan versi yang berfungsi dari setiap versi menggunakan variabel tabel
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
sumit
sumber
-1
 select salary from (
 select  Salary, ROW_NUMBER() over (order by Salary desc) rn from Employee 
 ) t where t.rn = 2
Aziz Khan
sumber
3
Selamat datang di Stack Overflow! Meskipun cuplikan kode ini mungkin bisa menjadi solusinya, menyertakan penjelasan sangat membantu untuk meningkatkan kualitas posting Anda. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa mendatang, dan orang-orang itu mungkin tidak tahu alasan saran kode Anda.
Johan
Harap tambahkan beberapa konteks ke cuplikan kode untuk kepentingan pembaca di masa mendatang.
DebanjanB