Where Clause'da SQL Row_Number () işlevi


92

Row_Number()Where cümlesindeki fonksiyonla cevaplanmış bir soru buldum . Bir sorgu denediğimde şu hatayı alıyordum:

"Msg 4108, Düzey 15, Durum 1, Satır 1 Pencereli işlevler yalnızca SELECT veya ORDER BY yan tümcelerinde görünebilir."

İşte denediğim sorgu. Biri bunu nasıl çözeceğini biliyorsa lütfen bana bildirin.

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

10
ROW_NUMBER() OVER (ORDER BY employee_id) > 0her zaman değerlendirecekTRUE
Quassnoi

3
Evet, bu doğru. İstediğim zaman değiştirebileceğim durum için endişelenmiyorum. Önce sorgunun işe yaramasını istiyorum, sonra sıradaki sayıyı 500 ile 800 arasında tutmayı düşünüyorum ... teşekkürler

2
@Joseph: Neden CTE kullanmaktan kaçınmaya çalışıyorsun?
OMG Ponies

1
@rexem - SQL Server konusunda uzman değilim. Performansla ilgili birçok sorunla karşılaştıkları büyük bir projede bir takıma yardımcı olmaya çalışıyorum. UDF'leri ve CTE'leri kullanıyorlar. Tablodan birinde sadece 5000 kayıt var ve 5 kullanıcı bir aramaya erişiyorsa, geri alınması bir dakikadan fazla zaman alıyor. Bir zaman başarısız olur ve zaman aşımına uğrar. Bu yüzden, CTE ve UDF'lerden kaçınmaya ve performans sorunlarını çözebilecek basit bir SQL sorgusu bulmaya çalışıyorum.

1
Merhabalar, Lütfen, row_number () 'yı farklı bir şekilde kullanan cevaplar için aşağıda yayınladığım bağlantıya bakın. Birisi ilk sorgumu bağlantıdakiyle karşılaştırabilir mi? Yardım

Yanıtlar:


96

Bu sorunu aşmak için, select deyiminizi bir CTE'ye sarın ve ardından CTE'ye karşı sorgulama yapabilir ve where cümlesinde pencereli işlevin sonuçlarını kullanabilirsiniz.

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'den kaçınmaya çalışıyorum. Aradığım en kötü durum bu. teşekkürler

3
CTE yerine alt sorgu kullanırsanız daha hızlı çalışabilir. Bazı durumlarda 1,5 kat daha iyi performans gördüm
Brian Webster

3
CTE SELECT'te TOP da olmalıdır, aksi takdirde SQL 2008 Server ORDER BY nedeniyle sorguyu yürütmez (TOP kullanılmadıkça desteklenmez)
Muflix

2
SQL2005 kullanıyorum (ugh) - FROM'dan sonra "ORDER BY" seçeneğini bırakarak "TOP" kullanımını önleyebilirim. Zaten OVER'den sonra (Order By) ile gereksizdir.
Joe B

Ben kullanmak için bir yol yoktur diliyordum ROW_NUMBER()içinde WHERE:( CTE olmadan maddesi
Celal

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

Bu filtrenin gereksiz olduğuna dikkat edin: ROW_NUMBER()baştan başlar 1ve her zaman büyüktür 0.


2
@ DavideChicco.it: SQL Server'da, türetilmiş tablolar bir takma ad gerektirir ( AS qonun yerine yazmalıydım , ancak bu da işe yarar ).
Quassnoi

2
Okunabilirlik, takma adları adlandırırken sahip olduğum bir odak noktasıdır. Rn'yi RowNumber olarak ve q'yu DerivedTable olarak ve where cümlesini DerivedTable.RowNumber> 0 olarak yazabilirsiniz. Bence bu, kodun zihninizde taze olmadığı 6 ay içinde çok daha az kafa karıştırıcı olacak.
Edward Comeau

2
@EdwardComeau: rnBu günlerde satır numarası için evrensel olarak kabul edilen bir kısaltmadır. Google arama dizesine "satır_sayısı üzerinde ..." yazmayı deneyin ve size ne önerdiğini görün.
Quassnoi

3
@Quassnoi, okunabilirlik, iyi kodlamanın anahtarıdır ve rn'yi (veya diğer kısaltılmış takma adları) çevirmenin bilişsel çabası, kendiniz ve kodunuzu koruyan insanlar için bir araya gelir. NB, Microsoft ilk vuruş, SEÇ SAYISI () SEÇİN () OVER (SİPARİŞ TARAFINDAN SİPARİŞ VER) Satır, ... Ayrıca daha önce rn ile karşılaşmadım, bu nedenle "evrensel" deki kilometreniz değişebilir.
Edward Comeau

1
@Quassnoi ve ikinci hit, SO makalesi - stackoverflow.com/questions/961007/how-do-i-use-row- rn ;-) değil birkaç varyasyonu numaralandır
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

Sanırım böyle bir şey istiyorsun:

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

4
Yukarıdaki sorgu sizin için işe yaramazsa, tablo için bir takma ad oluşturun. İkinci son satırı değiştirerek From V_EMPLOYEE) Atakma ad olarak A ekleyin.
Hammad Khan

7

Bir satır içi görünümün mü yoksa CTE'nin mi daha hızlı olacağına ilişkin rexem'in yanıtı hakkındaki yorumlara yanıt olarak, sorguları bir tablo kullanmak için yeniden düzenledim ve herkesin sahip olduğu 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

Üretilen sorgu planları tamamen aynıydı. Her durumda, sorgu iyileştiricisinin, en azından CTE'nin satır içi görünümle basit bir şekilde değiştirilmesinde veya tam tersi durumda aynı planla ortaya çıkmasını beklerdim.

Elbette, bir fark olup olmadığını görmek için kendi sisteminizde kendi sorgularınızı deneyin.

Ayrıca, row_number()where cümlesi, Stack Overflow'da verilen yanıtlarda yaygın bir hatadır. Select cümlesi işlenene row_number()kadar mantıksal olarak kullanılamaz. İnsanlar bunu unutur ve cevabı test etmeden cevapladıklarında cevap bazen yanlıştır. (Suçlu olduğum bir suçlama.)


1
Thx Shannon. SQL Server'ın hangi sürümünü kullanıyordunuz?
OMG Ponies

1
Yani bu bağlantıda verilen cevabın yanlış olduğu anlamına mı geliyor? Ancak, soruyu gönderen kişi bunun işe yaradığını kabul etti .. Şaşırtıcı .. :-)

2
@Joseph, ancak bağlantılı soruda OP tarafından yayınlanan başka bir yanıta bakarsanız, kabul edilen yanıtla aynı olmayan bir kod sürümüne bağlantı verdiğini göreceksiniz. Girildiği gibi gitmese bile cevabı neden kabul ettiğini bilmiyorum. Belki kabul edildikten sonra bir noktada düzenlenmiştir, belki de tamamen doğru olmasa da devam etmesi için yeterliydi.
Shannon Kıdem Tazminatı

1
@Rexem: Hem SQL Server 2005 hem de SQL Server 2008. Önceki sürümler CTE'leri veya ROW_NUMBER ()
Shannon Ayrımı

6

Bir CTE veya Alt Sorgu kullanımını gösteren tüm yanıtların bunun için yeterli düzeltmeler olduğunu düşünüyorum, ancak OP'nin neden bir sorunu olduğu konusunda kimsenin kalbine indiğini görmüyorum. OP'nin önerdiği şeyin işe yaramamasının nedeni, buradaki mantıksal sorgu işleme sırasından kaynaklanmaktadır:

  1. FROM
  2. AÇIK
  3. KATILMAK
  4. NEREDE
  5. GRUPLAMA
  6. KÜP / ROLLUP İLE
  7. SAHİP
  8. SEÇ
  9. DISTINCT
  10. TARAFINDAN SİPARİŞ
  11. ÜST
  12. OFSET / FETCH

Bunun cevaba büyük katkı sağladığına inanıyorum, çünkü bunun gibi sorunların neden ortaya çıktığını açıklıyor. birçok işlev için CTE veya Alt Sorgu WHEREyapmadan önce her zaman işlenir SELECT. Bunu SQL Server'da çok göreceksiniz.


4

CTE kullanarak (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

Satır İçi görünümü / CTE Eşdeğeri Olmayan Alternatifi Kullanma:

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
Performansta hangisi daha iyi? CTE veya alt sorgu mu kullanıyorsunuz? teşekkürler

1
Shannon'ın cevabına bakın - testinde eşitler.
OMG Ponies

6
Hayır, daha hızlı değil. In SQL Server, CTE's ve inline görünümleri aynı şeydir ve aynı performansa sahiptir. A'da deterministik olmayan işlevler kullanıldığında CTE, her çağrıda yeniden değerlendirilir. A'nın maddileştirilmesini zorlamak için kirli numaralar kullanmak gerekir CTE. Blogumda bu makalelere bakın: explainextended.com/2009/07/28/... explainextended.com/2009/05/28/generating-xml-in-subqueries
Quassnoi

2

OP'nin soruya verdiği cevaba göre:

Lütfen bu bağlantıya bakın. Soruyu soran kişi için işe yarayan farklı bir çözümü var. Böyle bir çözüm bulmaya çalışıyorum.

SQL Server 2005'te ROW_NUMBER () OVER () kullanılarak farklı sütunlarda sıralama kullanılarak sayfalandırılmış sorgu

~ Joseph

"yöntem 1" OP'nin bağlantılı sorudaki sorgusu gibidir ve "yöntem 2" seçilen yanıttan gelen sorgu gibidir. Seçilen yanıttaki kod, çalışması için değiştirildiğinden, gerçekte neler olup bittiğini görmek için bu yanıtla bağlantılı koda bakmanız gerekiyordu. Bunu dene:

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

ÇIKTI:

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 yöntemini kullanırken, yöntem 1'in Toplam Alt Ağaç Maliyeti 0.08424953 iken, yöntem 2 0.02627153'tür. yöntem 2 üç kattan fazla daha iyiydi.
KM.

1
@rexem, hem yöntem 1 hem de 2 CTE'leri kullanır, satırları sayfalandırma ve sıralama biçimleri farklıdır. Bu asıl sorunun, OP'nin bağlantı verdiği sorudan neden bu kadar farklı olduğundan emin değilim (OP'nin bu sorunun cevabında), ancak cevabım OP'nin atıfta bulunduğu bağlantıya dayalı bir çalışma kodu yaratıyor
KM.

1
Teşekkürler, eski gönderiyi ve bu cevapları karşılaştırmaya çalışıyorum. [Bunu nasıl biçimlendireceğimi bilmiyorum] İşte Tomalak'ın verdiği cevap. stackoverflow.com/questions/230058?sort=votes#sort-top Bu yanlış mı? Cevabın yalnızca yarısını gönderdiyse, sorgumu daha iyi performansla yapma yöntemiyle nasıl ilerleyeceğim? Lütfen devam etmem için biraz daha ışık verin .. teşekkürler

@Joseph, sağladığınız bağlantıdaki seçili yanıt ( stackoverflow.com/questions/230058?sort=votes#sort-top ), soruyu soran kişinin yanıtında çalışırken sağladığı çalışma kodundan farklıdır: stackoverflow.com/ sorular / 230058 /… bu cevabı okursanız, kodlarına bir bağlantı göreceksiniz: pastebin.com/f26a4b403 ve Tomalak'ın sürümlerine bir bağlantı: pastebin.com/f4db89a8e cevabımda kullanarak her sürümün çalışan bir sürümünü sağlıyorum tablo değişkenleri
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
Stack Overflow'a hoş geldiniz! Bu kod parçacığı çözüm olabilir, ancak bir açıklama da dahil olmak üzere yayınınızın kalitesini artırmaya gerçekten yardımcı olur. Gelecekte okuyucular için soruyu yanıtlayacağınızı ve bu kişilerin kod önerinizin nedenlerini bilmeyebileceklerini unutmayın.
Johan

Lütfen gelecekteki okuyucuların yararına olması için kod pasajına biraz bağlam ekleyin .
DebanjanB
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.