MySQL mendapatkan posisi baris di ORDER BY


88

Dengan tabel MySQL berikut ini:

+-----------------------------+
+ id INT UNSIGNED             +
+ name VARCHAR(100)           +
+-----------------------------+

Bagaimana cara memilih satu baris DAN posisinya di antara baris lain dalam tabel, jika diurutkan berdasarkan name ASC. Jadi jika tabel datanya terlihat seperti ini, bila diurutkan berdasarkan nama:

+-----------------------------+
+ id | name                   +
+-----------------------------+
+  5 | Alpha                  +
+  7 | Beta                   +
+  3 | Delta                  +
+ .....                       +
+  1 | Zed                    +
+-----------------------------+

Bagaimana saya bisa memilih Betabaris yang mendapatkan posisi saat ini dari baris itu? Kumpulan hasil yang saya cari akan menjadi seperti ini:

+-----------------------------+
+ id | position | name        +
+-----------------------------+
+  7 |        2 | Beta        +
+-----------------------------+

Saya dapat melakukan yang sederhana SELECT * FROM tbl ORDER BY name ASClalu menghitung baris dalam PHP, tetapi tampaknya sia-sia untuk memuat kumpulan hasil yang berpotensi besar hanya untuk satu baris.



Jawaban:


122

Gunakan ini:

SELECT x.id, 
       x.position,
       x.name
  FROM (SELECT t.id,
               t.name,
               @rownum := @rownum + 1 AS position
          FROM TABLE t
          JOIN (SELECT @rownum := 0) r
      ORDER BY t.name) x
 WHERE x.name = 'Beta'

... untuk mendapatkan nilai posisi yang unik. Ini:

SELECT t.id,
       (SELECT COUNT(*)
          FROM TABLE x
         WHERE x.name <= t.name) AS position,
       t.name    
  FROM TABLE t      
 WHERE t.name = 'Beta'

... akan memberikan nilai yang sama pada ikatan. YAITU: Jika ada dua nilai di tempat kedua, keduanya akan memiliki posisi 2 ketika kueri pertama akan memberikan posisi 2 ke salah satunya, dan 3 ke yang lain ...


@actual: Tidak ada yang bisa dikatakan - tidak ada alternatif, selain pindah ke pesaing yang mendukung fungsi analitik (PostgreSQL, Oracle, SQL Server, DB2 ...)
OMG Ponies

2
@OMGPonies Lupakan saja koma setelahnya position, tapi sempurna.
pierallard

Saya tahu ini posting yang cukup lama, tetapi saya membutuhkan solusi serupa. Namun, saat menggunakan gabungan dalam, kelompokkan dan urutkan menurut, bidang "posisi" mengabaikan ini dan nilainya tercampur. Ada solusi?
Daniel

@Daniel Anda harus mengajukan pertanyaan baru dan mungkin merujuk ke pertanyaan ini.
PeerBr

@ Sebenarnya, karena ini adalah kueri untuk satu baris, seharusnya tidak ada masalah kinerja yang signifikan di sini. Ini berbeda jika Anda mencoba untuk mendapatkan peringkat dari daftar lengkap, tetapi Anda bisa saja "menipu" dan menggunakan peringkat implisit dengan mengurutkan berdasarkan poin.
AgmLauncher

20

Inilah satu-satunya cara yang dapat saya pikirkan:

SELECT `id`,
       (SELECT COUNT(*) FROM `table` WHERE `name` <= 'Beta') AS `position`,
       `name`
FROM `table`
WHERE `name` = 'Beta'

2
1 Bagus trik ... Namun Anda mungkin akan ingin menggunakan name <= 'Beta'bukannya
Daniel Vassallo

Pendekatan ini akan memberikan nilai yang sama positionuntuk ikatan.
OMG Ponies

(Menghapus komentar saya sebelumnya - Saya salah) ... Bagaimana jika Anda menambahkan LIMIT 1di sana? Jika terjadi seri, Anda hanya akan mendapatkan satu baris dengan posisi terakhir seri.
Daniel Vassallo

Jika OP dapat menjamin bahwa namebidang itu unik - maka tidak ada alasan untuk membuat kueri lebih kompleks. Jika dia tidak bisa - maka mari kita tunggu ekspektasi hasilnya untuk nama seri.
zerkms

8

Jika kueri sederhana dan ukuran kumpulan hasil yang dikembalikan berpotensi besar, Anda dapat mencoba membaginya menjadi dua kueri.

Kueri pertama dengan kriteria pemfilteran yang dipersempit hanya untuk mengambil data dari baris itu, dan kueri kedua menggunakan klausa COUNTwith WHEREuntuk menghitung posisi.

Misalnya dalam kasus Anda

Pertanyaan 1:

SELECT * FROM tbl WHERE name = 'Beta'

Pertanyaan 2:

SELECT COUNT(1) FROM tbl WHERE name >= 'Beta'

Kami menggunakan pendekatan ini dalam tabel dengan catatan 2M dan ini jauh lebih skalabel daripada pendekatan OMG Pony.


5

Jawaban lain sepertinya terlalu rumit bagi saya.

Inilah contoh mudahnya , katakanlah Anda memiliki tabel dengan kolom:

userid | points

dan Anda ingin mengurutkan userid berdasarkan poin dan mendapatkan posisi baris ("peringkat" pengguna), lalu Anda menggunakan:

SET @row_number = 0;

SELECT 
    (@row_number:=@row_number + 1) AS num, userid, points
FROM
    ourtable
ORDER BY points DESC

num memberi Anda posisi baris (peringkat).

Jika Anda memiliki MySQL 8.0+, Anda mungkin ingin menggunakan ROW_NUMBER ()


2

Posisi baris dalam tabel menunjukkan berapa banyak baris yang "lebih baik" dari baris yang ditargetkan.

Jadi, Anda harus menghitung baris-baris itu.

PILIH COUNT (*) + 1 DARI tableMANAname <'Beta'

Jika terjadi seri, posisi tertinggi dikembalikan.

Jika Anda menambahkan baris lain dengan nama yang sama dari "Beta" setelah baris "Beta" yang ada, maka posisi yang dikembalikan akan tetap 2, karena mereka akan berbagi tempat yang sama dalam klasifikasi.

Semoga ini membantu orang-orang yang akan mencari sesuatu yang serupa di masa depan, karena saya yakin pemilik pertanyaan sudah memecahkan masalahnya.


2

Saya memiliki masalah yang sangat mirip, itulah mengapa saya tidak akan mengajukan pertanyaan yang sama, tetapi saya akan berbagi di sini apa yang telah saya lakukan, saya juga harus menggunakan grup oleh, dan memesan oleh AVG. Ada siswa, dengan tanda tangan dan socore, dan saya harus memeringkat mereka (dengan kata lain, saya menghitung AVG terlebih dahulu, lalu memesannya di DESC, dan akhirnya saya perlu menambahkan posisi (peringkat untuk saya), jadi saya lakukan sesuatu yang sangat mirip sebagai jawaban terbaik sini, dengan sedikit perubahan yang menyesuaikan dengan masalah saya):

Saya akhirnya menempatkan kolom position(peringkat untuk saya) di PILIHAN eksternal

SET @rank=0;
SELECT @rank := @rank + 1 AS ranking, t.avg, t.name
  FROM(SELECT avg(students_signatures.score) as avg, students.name as name
FROM alumnos_materia
JOIN (SELECT @rownum := 0) r
left JOIN students ON students.id=students_signatures.id_student
GROUP BY students.name order by avg DESC) t 

Jawaban ini lebih mudah dipahami daripada jawaban yang diterima. +1
Eric Seastrand

1

Saya telah melalui jawaban yang diterima dan tampaknya agak rumit jadi inilah versi yang disederhanakan.

SELECT t,COUNT(*) AS position FROM t      
 WHERE name <= 'search string' ORDER BY name

0

Saya memiliki jenis masalah yang serupa di mana saya memerlukan peringkat ( Indeks ) tabel order by votes desc. Berikut ini bekerja dengan baik untuk saya.

Select *, ROW_NUMBER() OVER(ORDER BY votes DESC) as "rank"
From "category_model"
where ("model_type" = ? and "category_id" = ?)

-9

Mungkin yang Anda butuhkan adalah dengan menambahkan sintaks

LIMIT

jadi gunakan

SELECT * FROM tbl ORDER BY name ASC LIMIT 1

jika Anda hanya perlu satu baris ..


jawaban ini tidak menyelesaikan masalah di sini. Anda dapat mempertimbangkan untuk menghapusnya
Damián Rafael Lattenero
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.