MySQL'de ROW_NUMBER ()


281

MySQL'de SQL Server işlevini çoğaltmanın güzel bir yolu var mı ROW_NUMBER()?

Örneğin:

SELECT 
    col1, col2, 
    ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY col3 DESC) AS intRow
FROM Table1

Sonra, örneğin, her çift intRowiçin en yüksek col3olan tek bir satır elde etmek için 1 ile sınırlamak için bir koşul ekleyebilirim (col1, col2).


9
greatest-n-per-groupSizi benzer sorulara yönlendirmek için etiketlendi .
Bill Karwin

1
Sql-ServerBu, birleştirilmiş etiket aramasında en yüksek oyu alan öğe olduğu için kaldırıldı , ancak SQL Server ile gerçekten alakalı değil.
Martin Smith

basit bir mysql satır numarası işlevi için datamakessense.com/mysql-rownum-row-number-function adresini ziyaret
AdrianBR

MySQL için, tek etkili çözümler mysql.rjweb.org/doc.php/groupwise_max . Birçok kamuya açık çözüm, tam bir masa taraması (veya daha kötüsü) gerektirir.
Rick James

2
Gerçek utanmadan kendini tanıtan çözüm analitik fonksiyon ROW_NUMBER, RANK, DESNSE_RANK MySQL
Kenneth Xu

Yanıtlar:


102

Her (col1, col2) çifti için tek en yüksek col3 satır istiyorum.

Bu grup halinde bir maksimum , en sık sorulan SQL sorularından biri (kolay olması gerektiği gibi görünüyor, ama aslında öyle değil).

Sık sık bir boş-katılma için tombul:

SELECT t0.col3
FROM table AS t0
LEFT JOIN table AS t1 ON t0.col1=t1.col1 AND t0.col2=t1.col2 AND t1.col3>t0.col3
WHERE t1.col1 IS NULL;

“Eşleşen col1, col2 ile daha yüksek bir col3'e sahip başka satır bulunmayan tablodaki satırları alın.” (Bunu fark edeceksiniz ve birden fazla satır aynı col1, col2, col3'e sahipse, grup-maksimum diğer çözümlerin çoğu birden çok satır döndürecektir. Bu bir sorunsa, bazı son işlemlere ihtiyacınız olabilir.)


2
Peki bir (col1, col2) çifti için iki maksimum col3 değeri varsa ne olur? Sonunda iki sıra olur.
Paul

@ Paul: evet! Daha önce bir tik cevabına bir not ekledim. Genellikle uygulama katmanında istenmeyen ekstra satırları rasgele bir temelde kolayca bırakabilirsiniz, ancak hepsi aynı col3 ile çok sayıda satırınız varsa sorunlu olabilir.
bobince

1
Bobince, çözüm burada SO üzerinde oldukça popüler oldu, ama bir sorum var. Çözüm temelde biri aşağıdaki sorgu ile en büyük kimliği bulmaya çalışacağınız gibi aynıdır: Tek bir satır bulmak için IS NULL karşılaştırmaları SELECT t1.id FROM test t1 LEFT JOIN test t2 ON t1.id>t2.id WHERE t2.id IS NULL;gerektirmez n*n/2 + n/2mi? Görmediğim herhangi bir optimizasyon oldu mu? Ben benzer bir soru Bill başka bir iş parçacığında sormaya çalıştı ama görmezden görünüyor.
newtover

2
@Paul - Grup başına maksimum değerle eşleşen ve yalnızca bir tane almak istediğiniz birden fazla satırın bulunduğu durumu ele almak için, her zaman ON anahtar mantığına birincil anahtarı ekleyerek kravat kopmasını ... SELECT t0.col3 FROM table AS t0 SOL BİRLEŞTİR tablo AS t1 AÇIK t0.col1 = t1.col1 VE t0.col2 = t1.col2 AND (t1.col3, t1.pk)> (t0.col3, t0.pk) NEREDE t1.col1 NULL;
Jon Armstrong - Xgc

2
Bu daha okunabilir olurSELECT t0.col3 FROM table AS t0 WHERE NOT EXISTS (select 1 from table AS t1 ON t0.col1=t1.col1 AND t0.col2=t1.col2 AND t1.col3>t0.col3)
wrschneider

204

MySQL'de sıralama işlevi yoktur. Alabileceğiniz en yakın değişken kullanmaktır:

SELECT t.*, 
       @rownum := @rownum + 1 AS rank
  FROM YOUR_TABLE t, 
       (SELECT @rownum := 0) r

Peki bu benim durumumda nasıl çalışır? Ben col1 ve col2 her biri için iki değişken gerekir? Col1 değiştiğinde Col2 bir şekilde sıfırlanması gerekir ..?

Evet. Oracle olsaydı, bir sonraki değerde zirve yapmak için LEAD işlevini kullanabilirsiniz. Neyse ki, Quassnoi MySQL'de uygulamanız gerekenlerin mantığını kapsar .


1
Hmm .... peki bu benim durumumda nasıl çalışır? Ben col1 ve col2 her biri için iki değişken gerekir? Col1 değiştiğinde Col2 bir şekilde sıfırlanması gerekir ..?
Paul

Teşekkürler ... yukarıda söylediğim gibi, bu cevap aynı şekilde kabul edilir bobince's, ama sadece birini işaretleyebilirim :-)
Paul

9
Aynı ifadede kullanıcı tanımlı değişkenlere atanmak ve bunlardan okumak güvenilir değildir. bu burada belgelenmiştir: dev.mysql.com/doc/refman/5.0/en/user-variables.html : "Genel bir kural olarak, hiçbir zaman kullanıcı değişkenine bir değer atamamalı ve aynı ifadedeki değeri okumalısınız. Beklediğiniz sonuçları alabilirsiniz, ancak bu garanti edilmez. Kullanıcı değişkenlerini içeren ifadelerin değerlendirme sırası tanımlanmamıştır ve belirli bir ifadede bulunan öğelere bağlı olarak değişebilir. "
Roland Bouman

1
@Roland: Sadece küçük veri kümelerinde test ettim, herhangi bir sorun yaşamadım. MySQL henüz işlevselliği ele
almıyor

2
Roland'ın belirttiği gibi, bu tanımsız bir davranış gibi görünüyor. örneğin, bu denedim bir tablo için tamamen yanlış sonuçlar verir:SELECT @row_num:=@row_num+1 AS row_number, t.id FROM (SELECT * FROM table1 WHERE col = 264 ORDER BY id) t, (SELECT @row_num:=0) var;
jberryman

81

Her zaman bu kalıbı takip ediyorum. Bu tablo verildiğinde:

+------+------+
|    i |    j |
+------+------+
|    1 |   11 |
|    1 |   12 |
|    1 |   13 |
|    2 |   21 |
|    2 |   22 |
|    2 |   23 |
|    3 |   31 |
|    3 |   32 |
|    3 |   33 |
|    4 |   14 |
+------+------+

Bu sonucu alabilirsiniz:

+------+------+------------+
|    i |    j | row_number |
+------+------+------------+
|    1 |   11 |          1 |
|    1 |   12 |          2 |
|    1 |   13 |          3 |
|    2 |   21 |          1 |
|    2 |   22 |          2 |
|    2 |   23 |          3 |
|    3 |   31 |          1 |
|    3 |   32 |          2 |
|    3 |   33 |          3 |
|    4 |   14 |          1 |
+------+------+------------+

Tanımlanmış herhangi bir değişkene ihtiyaç duymayan bu sorguyu çalıştırarak:

SELECT a.i, a.j, count(*) as row_number FROM test a
JOIN test b ON a.i = b.i AND a.j >= b.j
GROUP BY a.i, a.j

Umarım yardımcı olur!


1
sütunlar VARCHAR veya CHAR ise, bu yapı ile nasıl başa çıkabilirsiniz?
Tushar

3
Harikasın Mosty, tam olarak bunun için arıyorum
luckykrrish

Bu cevabı row_number için mantığınızı kullanarak verdim. Teşekkürler.
Utsav

Operatörler @Tushar <, >, <=, >=sap CHAR ve alfabetik sırayla ilgili VARCHAR veri tipleri; Beklediğiniz, tam olarak aradığınız şey.
alex

1
@AlmazVildanov Bu sorguyu filtrelemek için bir alt sorgu olarak kullanabilmelisiniz row_numbers <= 2 Ve bu cevap için çok teşekkürler Mosty, mükemmel!
Zax

61
SELECT 
    @i:=@i+1 AS iterator, 
    t.*
FROM 
    tablename AS t,
    (SELECT @i:=0) AS foo

1
İlk: = @OMG Ponies cevabından eksik görünüyor. Bu Peter Johnson'u gönderdiğin için teşekkürler.
sholsinger

Sanırım (SELECT @i: = 0) AS foo FROM deyiminde ilk tablo olmalıdır, özellikle diğer tablolar alt
seçimler

Neden ".. foo" a ihtiyacınız var?
Tom Chiverton

@TomChiverton Eksikse, şunu alırsınız: "Hata Kodu: 1248. Türetilmiş her tablonun kendi diğer adı olmalıdır"
ExStackChanger

1
Buradaki rütbe ataması tamamen tanımsız ve bu soruya bile cevap vermiyor
jberryman

27

Bu makaleye göz atın, SQL ROW_NUMBER () 'in bir bölümle MySQL içinde nasıl taklit edileceğini gösterir. Aynı senaryoya bir WordPress Uygulamasında rastladım. ROW_NUMBER () gerekli ve orada değildi.

http://www.explodybits.com/2011/11/mysql-row-number/

Makaledeki örnek, alana göre tek bir bölüm kullanmaktır. Ek alanlara göre bölümlemek için şöyle bir şey yapabilirsiniz:

  SELECT  @row_num := IF(@prev_value=concat_ws('',t.col1,t.col2),@row_num+1,1) AS RowNumber
         ,t.col1 
         ,t.col2
         ,t.Col3
         ,t.col4
         ,@prev_value := concat_ws('',t.col1,t.col2)
    FROM table1 t,
         (SELECT @row_num := 1) x,
         (SELECT @prev_value := '') y
   ORDER BY t.col1,t.col2,t.col3,t.col4 

Concat_ws kullanımı null değerlerini işler. Bunu int, date ve varchar kullanarak 3 alana karşı test ettim. Bu yardımcı olur umarım. Bu sorguyu bozup açıklarken makaleye göz atın.


1
Muhteşem. Bu aslında bölümleme yapar. Çok kullanışlı
Stuart Watt

1
Kendi kendine katılma ile karşılaştırıldığında, bu çok daha etkilidir, ancak mantıkla ilgili bir sorun vardır, row_num hesaplanmadan önce sıra oluşmalıdır, concat da gerekli değildir. `` SELECT @row_num: = EĞER (@ prev_col1 = t.col1 AND @ prev_col2 = t.col2), @ row_num + 1, 1) AS RowNumber, t.col1, t.col2, t.col3, t.col4 , @ prev_col1: = t.col1, @ prev_col2: = t.col2 FROM (SELECT * FROM table1 ORDER BY col1, col2, col3) t, (SELECT @row_num: = 1, @ prev_col1: = '', @ prev_col2: = '') var ``
Kenneth Xu

Eğer bu bir alt sorguyu koymak için tu gerekiyorsa, limit 18446744073709551615kuvvet order byyan tümcesi ekleyin .
xmedeko

concat_wsile boş dize ''tehlikelidir: concat_ws('',12,3) = concat_ws('',1,23). Bazı ayırıcı '_'kullanmak veya @Kenneth Xu çözümünü kullanmak daha iyidir.
17'de xmedeko

op'un bağlantısı öldü; burada bir arşiv
user2426679

25

Gönderen MySQL 8.0.0ve üzeri özgün olarak pencereli işlevleri kullanabilirsiniz.

1.4 MySQL 8.0'daki Yenilikler :

Pencere fonksiyonları.

MySQL artık bir sorgudaki her satır için o satırla ilgili satırları kullanarak bir hesaplama yapan pencere işlevlerini desteklemektedir. Bunlar RANK (), LAG () ve NTILE () gibi işlevleri içerir. Ayrıca, mevcut birkaç toplama işlevi artık pencere işlevleri olarak kullanılabilir; örneğin, SUM () ve AVG ().

ROW_NUMBER () over_cuseuse :

Bölümündeki geçerli satırın sayısını döndürür. Satır numaraları 1 ile bölüm satırı sayısı arasında değişir.

ORDER BY, satırların numaralandırılma sırasını etkiler. ORDER BY olmadan, satır numaralandırma belirsizdir.

Demo:

CREATE TABLE Table1(
  id INT AUTO_INCREMENT PRIMARY KEY, col1 INT,col2 INT, col3 TEXT);

INSERT INTO Table1(col1, col2, col3)
VALUES (1,1,'a'),(1,1,'b'),(1,1,'c'),
       (2,1,'x'),(2,1,'y'),(2,2,'z');

SELECT 
    col1, col2,col3,
    ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY col3 DESC) AS intRow
FROM Table1;

DBFiddle Demosu


1
iç çekiş ... sonunda!
Used_By_Already

15

Ayrıca sorgu kodunda küçük değişikliklerle Mosty Mostacho'nun çözümüne oy verirdim:

SELECT a.i, a.j, (
    SELECT count(*) from test b where a.j >= b.j AND a.i = b.i
) AS row_number FROM test a

Hangi aynı sonucu verecektir:

+------+------+------------+
|    i |    j | row_number |
+------+------+------------+
|    1 |   11 |          1 |
|    1 |   12 |          2 |
|    1 |   13 |          3 |
|    2 |   21 |          1 |
|    2 |   22 |          2 |
|    2 |   23 |          3 |
|    3 |   31 |          1 |
|    3 |   32 |          2 |
|    3 |   33 |          3 |
|    4 |   14 |          1 |
+------+------+------------+

tablo için:

+------+------+
|    i |    j |
+------+------+
|    1 |   11 |
|    1 |   12 |
|    1 |   13 |
|    2 |   21 |
|    2 |   22 |
|    2 |   23 |
|    3 |   31 |
|    3 |   32 |
|    3 |   33 |
|    4 |   14 |
+------+------+

Sorgunun JOIN ve GROUP BY kullanmadığı tek farkla, bunun yerine iç içe seçime dayanarak.


Bunun daha iyi olması mı gerekiyor? İkisi de ikinci dereceden gibi görünüyor, ancak EXPLAIN çıktısını nasıl yorumlayacağından emin değilim
jberryman

Aslında, iç içe seçimlerin MySQL'de çok iyi optimize edilmediği bilinmektedir, bu nedenle bu anwser sadece bir sorgulama tekniğinin gösterilmesi içindir. Yukarıdaki değişken tabanlı örnekler, çoğu pratik durum için daha iyi çalışır, sanırım.
abcdn

1
Değişken tabanlı cevaplardan herhangi birinin aslında tanımlanmış davranışı kullandığına ikna olmuş değilim ...
jberryman

Üzgünüm, "tanımlı davranış" ile ne demek istediğini anladığımdan emin değilim. Yani sizin için işe yaramıyor mu, yoksa sadece belgelenmediğinden mi endişeleniyorsunuz?
abcdn

1
"Tanımsız davranış", çalışmanın belgelenmediği ve / veya çalışacağı garanti edilmediği belgelendirildiği anlamına gelir. Bu sayfadaki yorumlarda dokümantasyon alıntılarına ve bağlantılarına bakın. Bu belki bir (unsoundly) / tahminler / hipotezlerdir / fantaziler istediğini dönün. Uygulamanın belirli sürümleri için CASE artış ve değişkenlerini kullanan belirli sorgu ifadelerinin, koda bakarak Percona'daki programcılar tarafından çalıştığı gösterilmiştir. Bu herhangi bir sürümle değişebilir.
philipxy

12

Ben bir fonksiyon tanımlamak:

delimiter $$
DROP FUNCTION IF EXISTS `getFakeId`$$
CREATE FUNCTION `getFakeId`() RETURNS int(11)
    DETERMINISTIC
begin
return if(@fakeId, @fakeId:=@fakeId+1, @fakeId:=1);
end$$

o zaman yapabilirdim:

select getFakeId() as id, t.* from table t, (select @fakeId:=0) as t2;

Artık görünümlerde bulunamayan bir alt sorgunuz yok.


Tek bir sınırlama ile çalışır: Sorguyu birkaç kez yürütürseniz, aynı sonuç kümesi için sürekli artan fakeIds elde edersiniz
Stephan Richter

set @fakeId = 0 gönderebilirsiniz; sorguyu her çalıştırmak istediğinizde, optimal değil ama çalışıyor
jmpeace

DETERMINISTIC'i kaldırırsanız gerçekten garip bir sorun olur. Sonra tarafından sipariş kullanılırken fakeId yanlıştır. Bu neden?
Chris Muench

8

mysql'de row_number için sorgu

set @row_number=0;
select (@row_number := @row_number +1) as num,id,name from sbs

Bu GÜNCELLEME sorgularında kullanılabilir? Deniyorum ama "sütun için veri kesildi ..." hatası alıyorum.
Diego

1
Herkes UPDATE tarihinde kullanmakla ilgileniyorsa, çalışması için alt sorgu olarak kullanılması gerekir. UPDATE <table> SET <field> = (SELECT \ @row_number: = \ @row_number +1) ORDER BY <sipariş sütununuz>; Sıra sütunu, satırların değer sırasını belirler.
Diego

8

MySQL'de böyle bir işlev yoktur rownum, row_num()ancak yol aşağıdaki gibidir:

select 
      @s:=@s+1 serial_no, 
      tbl.* 
from my_table tbl, (select @s:=0) as s;

4

En iyi şekilde çalıştığım çözüm şöyle bir alt sorgu kullanmaktı:

SELECT 
    col1, col2, 
    (
        SELECT COUNT(*) 
        FROM Table1
        WHERE col1 = t1.col1
        AND col2 = t1.col2
        AND col3 > t1.col3
    ) AS intRow
FROM Table1 t1

PARTITION BY sütunları sadece '=' ile karşılaştırılır ve AND ile ayrılır. ORDER BY sütunları '<' veya '>' ile karşılaştırılır ve OR ile ayrılır.

Biraz pahalı olsa bile çok esnek buldum.


4

Rownumber işlevi taklit edilemez. Beklediğiniz sonuçları alabilirsiniz, ancak büyük olasılıkla bir aşamada hayal kırıklığına uğrayacaksınız. MySQL belgeleri şöyle diyor:

SELECT gibi diğer ifadeler için beklediğiniz sonuçları alabilirsiniz, ancak bu garanti edilmez. Aşağıdaki ifadede, MySQL'in önce @a'yı sonra ikinci bir atama yapacağını düşünebilirsiniz: SELECT @a, @a: = @ a + 1, ...; Bununla birlikte, kullanıcı değişkenlerini içeren ifadeler için değerlendirme sırası tanımlanmamıştır.

Saygılar Georgi.


Takip etmiyorum. "Konum olarak" @i: = @i + 1 ", konum olarak" ROW_NUMBER () üzerinden (toplamla (puan) desc) sıralama "yerine doğrudan bir değişiklik değil").
Tom Chiverton

1
@TomChiverton Davranışı tanımlanmadığı için, kılavuzun tam olarak dediği gibi.
philipxy


2

"PARTITION BY" bölümünü kapsayan basit bir cevap görmüyorum, işte benim:

SELECT
    *
FROM (
    select
        CASE WHEN @partitionBy_1 = l THEN @row_number:=@row_number+1 ELSE @row_number:=1 END AS i
        , @partitionBy_1:=l AS p
        , t.*
    from (
        select @row_number:=0,@partitionBy_1:=null
    ) as x
    cross join (
        select 1 as n, 'a' as l
        union all
        select 1 as n, 'b' as l    
        union all
        select 2 as n, 'b' as l    
        union all
        select 2 as n, 'a' as l
        union all
        select 3 as n, 'a' as l    
        union all    
        select 3 as n, 'b' as l    
    ) as t
    ORDER BY l, n
) AS X
where i > 1
  • ORDER BY deyimi ROW_NUMBER ihtiyacınızı yansıtmalıdır. Bu nedenle zaten açık bir sınırlama vardır: Bu formda aynı anda birkaç ROW_NUMBER "öykünme" olamaz.
  • "Hesaplanan sütun" un sırası önemlidir . Eğer mysql bu sütunu başka bir sırayla hesaplarsanız, çalışmayabilir.
  • Bu basit örnekte sadece bir tane koydum ama birkaç "PARTITION BY" parçasına sahip olabilirsiniz

        CASE WHEN @partitionBy_1 = part1 AND @partitionBy_2 = part2 [...] THEN @row_number:=@row_number+1 ELSE @row_number:=1 END AS i
        , @partitionBy_1:=part1 AS P1
        , @partitionBy_2:=part2 AS P2
        [...] 
    FROM (
        SELECT @row_number:=0,@partitionBy_1:=null,@partitionBy_2:=null[...]
    ) as x

1

Biraz geç ama cevap arayan birine de yardımcı olabilir ...

Satırlar / satır_sayısı örneği - herhangi bir SQL'de kullanılabilecek özyinelemeli sorgu arasında:

WITH data(row_num, some_val) AS 
(
 SELECT 1 row_num, 1 some_val FROM any_table --dual in Oracle
  UNION ALL
 SELECT row_num+1, some_val+row_num FROM data WHERE row_num < 20 -- any number
)
SELECT * FROM data
 WHERE row_num BETWEEN 5 AND 10
/

ROW_NUM    SOME_VAL
-------------------
5           11
6           16
7           22
8           29
9           37
10          46

2
Üzgünüz ama bildiğim kadarıyla MySQL ortak tablo ifadelerini desteklemiyor .
Álvaro González

şimdi ... @ ÁlvaroGonzález MySQL 8 sadece CTE ve pencere fonksiyonlarını destekliyor, bu nedenle bu cevap gerçekten eski MySQL sürümlerinde kullanmak mantıklı değil ..
Raymond Nijland

1

Bu, ROW_NUMBER () AND PARTITION BY tarafından sağlanan işlevselliklerin MySQL'de elde edilmesini sağlar

SELECT  @row_num := IF(@prev_value=GENDER,@row_num+1,1) AS RowNumber
       FirstName, 
       Age,
       Gender,
       @prev_value := GENDER
  FROM Person,
      (SELECT @row_num := 1) x,
      (SELECT @prev_value := '') y
  ORDER BY Gender, Age DESC

1

Ayrıca biraz geç ama bugün aynı ihtiyacı vardı, bu yüzden Google'da arama yaptım ve son olarak burada Pinal Dave'in makalesinde bulunan basit bir genel yaklaşım http://blog.sqlauthority.com/2014/03/09/mysql-reset-row -sayısı-için-her grup-bölme-ile sıralı-numarası /

Paul'ün orijinal sorusuna odaklanmak istedim (bu da benim sorunumdu), bu yüzden çözümümü çalışan bir örnek olarak özetledim.

Çünkü yeni bir grubun başlatılıp başlatılmadığını belirlemek için yineleme sırasında bir SET değişkeni oluşturacağım.

SELECT col1, col2, col3 FROM (
  SELECT col1, col2, col3,
         @n := CASE WHEN @v = MAKE_SET(3, col1, col2)
                    THEN @n + 1 -- if we are in the same group
                    ELSE 1 -- next group starts so we reset the counter
                END AS row_number,
         @v := MAKE_SET(3, col1, col2) -- we store the current value for next iteration
    FROM Table1, (SELECT @n := 0, @v := NULL) r -- helper table for iteration with startup values
   ORDER BY col1, col2, col3 DESC -- because we want the row with maximum value
) x WHERE row_number = 1 -- and here we select exactly the wanted row from each group

3, MAKE_SET'in ilk parametresinde SET'te (3 = 1 | 2) her iki değeri istediğim anlamına gelir. Tabii ki grupları inşa eden iki veya daha fazla sütunumuz yoksa MAKE_SET işlemini ortadan kaldırabiliriz. İnşaat tamamen aynı. Bu benim için gerektiği gibi çalışıyor. Açık gösteri için Pinal Dave'e çok teşekkürler.


1
O Not ORDER BYBir alt sorgu göz ardı edilebilir (bkz mariadb.com/kb/en/mariadb/... ). Bunun için önerilen çözüm LIMIT 18446744073709551615, bir tür zorlayan alt sorguyu eklemektir . Ancak bu performans sorunlarına neden olabilir ve gerçekten çok büyük tablolar için geçerli değildir :)
pnomolos

1

Bu da bir çözüm olabilir:

SET @row_number = 0;

SELECT 
    (@row_number:=@row_number + 1) AS num, firstName, lastName
FROM
    employees

Yine de herhangi bir bölümleme yapmaz ve daha yüksek bir alıntıdan önemli ölçüde farklı değildir
Caius Jard

1

MySQL, 8.0 ve sonraki sürümlerden beri ROW_NUMBER () yöntemini destekledi .

MySQL 8.0 veya üstünü kullanıyorsanız, ROW_NUMBER () işlevine bakın. Aksi takdirde, ROW_NUMBER () işlevini öykün.

Row_number (), ilk satır için 1'den başlayarak sıranın sıralı sayısını döndüren bir sıralama işlevidir.

eski sürüm için,

SELECT t.*, 
       @rowid := @rowid + 1 AS ROWID
  FROM TABLE t, 
       (SELECT @rowid := 0) dummy;

1

Önemli: Lütfen MySQL 8+ sürümüne geçmeyi düşünün ve tanımlı ve belgelenmiş ROW_NUMBER () işlevini kullanın ve MySQL'in özelliklerle sınırlı eski bir sürümüne bağlı eski korsanlardan kurtulun

Şimdi bu hacklerden biri:

Burada çoğunlukla sorgu içi değişkenleri kullanan cevaplar / hepsi belgelerin söylediği gerçeğini göz ardı ediyor gibi görünüyor (açıklama):

Yukarıdan aşağıya doğru sırayla değerlendirilmekte olan SELECT listesindeki öğelere güvenmeyin. Bir SELECT öğesinde değişken atama ve bunları başka bir SELECT öğesinde kullanma

Bu nedenle, yanlış cevabı atma riski vardır, çünkü genellikle

select
  (row number variable that uses partition variable),
  (assign partition variable)

Bunlar aşağıdan yukarıya değerlendirilirse, satır numarası çalışmayı durduracaktır (bölüm yok)

Bu nedenle, garantili bir yürütme sırasına sahip bir şey kullanmamız gerekiyor. NE ZAMAN DURUMDA GİRİN:

SELECT
  t.*, 
  @r := CASE 
    WHEN col = @prevcol THEN @r + 1 
    WHEN (@prevcol := col) = null THEN null
    ELSE 1 END AS rn
FROM
  t, 
  (SELECT @r := 0, @prevcol := null) x
ORDER BY col

Anahat ld olarak, prevcol atama sırası önemlidir - biz geçerli satırdan bir değer atamadan önce prevcol geçerli satırın değeri ile karşılaştırılmalıdır (aksi takdirde önceki satırın col değeri değil, geçerli satırların col değeri olurdu) .

Bunun nasıl birbirine uyduğu aşağıda açıklanmıştır:

  • İlk NE ZAMAN değerlendirilir. Bu satırın sütunu önceki satırın sütunu ile aynıysa, @r artırılır ve CASE'den döndürülür. Bu dönüş led değerleri @r. MySQL'in bir özelliği, atamanın @r'ye atanan şeyin yeni değerini sonuç satırlarına döndürmesidir.

  • Sonuç kümesindeki ilk satır için @prevcol null (alt sorguda null olarak başlatılır), bu nedenle bu yüklem yanlıştır. Bu ilk yüklem ayrıca col her değiştiğinde false değerini döndürür (geçerli satır önceki satırdan farklıdır). Bu, ikinci NE ZAMAN değerlendirilmesine neden olur.

  • İkinci WHEN yüklemi her zaman yanlıştır ve yalnızca @prevcol'e yeni bir değer atamak için var olur. Bu satırın col değeri önceki satırın col'sinden farklı olduğu için (bunu biliyoruz çünkü aynı olsaydı, ilk NE ZAMAN kullanılırsa), bir dahaki sefere test için yeni değeri atamamız gerekir. Ödev yapıldığından ve ödevin sonucu null ile karşılaştırıldığından ve null ile eşitlenmiş herhangi bir şey yanlış olduğundan, bu yüklem her zaman yanlıştır. Ancak en azından değerlendirmek, col değerini bu satırdan tutma görevini yaptı, böylece bir sonraki satırın col değerine göre değerlendirilebilir

  • İkinci NE ZAMAN yanlış olduğu için, (col) ile bölümlediğimiz sütunun değiştiği durumlarda, numarayı 1'den yeniden başlatarak @r için yeni bir değer veren ELSE'dir.

Bu durumun şu olduğu bir duruma geliyoruz:

SELECT
  t.*, 
  ROW_NUMBER() OVER(PARTITION BY pcol1, pcol2, ... pcolX ORDER BY ocol1, ocol2, ... ocolX) rn
FROM
  t

Genel forma sahiptir:

SELECT
  t.*, 
  @r := CASE 
    WHEN col1 = @pcol1 AND col2 = @pcol2 AND ... AND colX = @pcolX THEN @r + 1 
    WHEN (@pcol1 := pcol1) = null OR (@pcol2 := col2) = null OR ... OR (@pcolX := colX) = null THEN null
    ELSE 1 
  END AS rn
FROM
  t, 
  (SELECT @r := 0, @pcol1 := null, @pcol2 := null, ..., @pcolX := null) x
ORDER BY pcol1, pcol2, ..., pcolX, ocol1, ocol2, ..., ocolX

Dipnotlar:

  • Pcol içindeki p "bölüm" anlamına gelir, ocol içindeki o "düzen" anlamına gelir - genel formda görsel karmaşayı azaltmak için değişken adından "prev" i düşürdüm

  • Çevresindeki parantezler (@pcolX := colX) = nullönemlidir. Onlar olmadan @pcolX'e null atayacaksınız ve işler artık çalışmıyor

  • Önceki sütun karşılaştırmasının çalışması için sonuç kümesinin bölüm sütunları tarafından da sıralanması gerektiği bir uzlaşmadır. Böylece rownumber'ınızı bir sütuna göre sıralayamazsınız, ancak sonuç kümeniz diğerine sıralanabilir. Bunu alt sorgular ile çözebilirsiniz, ancak belgelerin LIMIT kullanılmadığı sürece alt sorgu sırasının göz ardı edilebileceğini ve bu etkinin verim

  • Yöntemin işe yaradığını test etmenin ötesine geçmedim, ancak ikinci NE ZAMAN'daki tahminlerin ortadan kaldırılacağı riski varsa (null ile karşılaştırılan herhangi bir şey null / false, öyleyse neden ödevi çalıştırmıyorsunuz) ve yürütülmedi , durur. Bu benim tecrübelerime göre görünmüyor, ancak yorumları memnuniyetle kabul edip makul bir şekilde ortaya çıkabilirse çözüm önereceğim

  • @PcolX oluşturan null'ları, @pcolX değişkenlerini oluşturan alt sorguda, sütunlarınızın gerçek türlerine dönüştürmek akıllıca olabilir: select @pcol1 := CAST(null as INT), @pcol2 := CAST(null as DATE)


Bunun bir gerekçesi yok. Aynı değişkene atanan ve aynı değişkenten okuyan diğer cevaplar gibi.
philipxy

Daha fazla ayrıntı phil sağlayabilir?
Caius Jard

Bu sayfadaki diğer yorumlarıma bakın. Googling 'site: stackoverflow.com 'philipxy' mysql değişkeni (dizi VEYA atama VEYA ödev OR yazma) okuma': Bir cevap benim tarafımdan & bir hata raporuna de bana göre bir yorum bağlantılı bu soruya kabul cevabı henüz derhal manuel tırnak nerede iddialarda ona aykırı bir şey yapmak sorun değil. Manuel yeniden değişkenleri ve atamayı okuyun.
philipxy


Endişenizi anlıyorum
Caius Jard

0

Bu en sağlam çözüm değildir - ancak yalnızca birkaç farklı değere sahip bir alanda bölümlenmiş bir sıra oluşturmak istiyorsanız, mantıksal olarak istediğiniz kadar çok değişken içeren bir durum kullanmak yararsız olabilir.

Böyle bir şey geçmişte benim için çalıştı:

SELECT t.*, 
   CASE WHEN <partition_field> = @rownum1 := @rownum1 + 1 
     WHEN <partition_field> = @rownum2 := @rownum2 + 1 
     ...
     END AS rank
FROM YOUR_TABLE t, 
   (SELECT @rownum1 := 0) r1, (SELECT @rownum2 := 0) r2
ORDER BY <rank_order_by_field>
;

Umarım mantıklıdır / yardımcı olur!


-1

Bu, birden fazla sütuna sahip olduğumda RowNumber oluşturmam için mükemmel çalışıyor. Bu durumda iki sütun.

SELECT @row_num := IF(@prev_value= concat(`Fk_Business_Unit_Code`,`NetIQ_Job_Code`), @row_num+1, 1) AS RowNumber, 
    `Fk_Business_Unit_Code`,   
    `NetIQ_Job_Code`,  
    `Supervisor_Name`,  
    @prev_value := concat(`Fk_Business_Unit_Code`,`NetIQ_Job_Code`)  
FROM (SELECT DISTINCT `Fk_Business_Unit_Code`,`NetIQ_Job_Code`,`Supervisor_Name`         
      FROM Employee    
      ORDER BY `Fk_Business_Unit_Code`, `NetIQ_Job_Code`, `Supervisor_Name` DESC) z,  
(SELECT @row_num := 1) x,  
(SELECT @prev_value := '') y  
ORDER BY `Fk_Business_Unit_Code`, `NetIQ_Job_Code`,`Supervisor_Name` DESC

-3
set @i = 1;  
INSERT INTO ARG_VALUE_LOOKUP(ARG_VALUE_LOOKUP_ID,ARGUMENT_NAME,VALUE,DESCRIPTION,UPDATE_TIMESTAMP,UPDATE_USER,VER_NBR,OBJ_ID) 
select @i:= @i+1 as ARG_VALUE_LOOKUP_ID,ARGUMENT_NAME,VALUE,DESCRIPTION,CURRENT_TIMESTAMP,'admin',1,UUID() 
FROM TEMP_ARG_VALUE_LOOKUP 
order by ARGUMENT_NAME;

1
Lütfen yanıtları biçimlendirmeyi deneyin ve ne yapmaya çalıştığınızla ilgili bazı ek bağlamlar verin. Şu anda, kötü biçimlendirilmiş metinden başka bir şey değil.
Yannick Meeus

2
Bunun orijinal soru ile herhangi bir ilişkisi yok gibi görünüyor. Kendinize ait bir sorunuz varsa, lütfen ayrı olarak sorun.
Jeroen Mostert

-5
SELECT 
    col1, col2, 
    count(*) as intRow
FROM Table1
GROUP BY col1,col2
ORDER BY col3 desc
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.