Con MySQL, come posso generare una colonna contenente l'indice del record in una tabella?


100

C'è un modo per ottenere il numero di riga effettivo da una query?

Voglio poter ordinare una tabella chiamata league_girl da un campo chiamato score; e restituisce il nome utente e l'effettiva posizione della riga di tale nome utente.

Voglio classificare gli utenti in modo da poter sapere dove si trova un determinato utente, ad es. Joe è posizione 100 su 200, cioè

User Score Row
Joe  100    1
Bob  50     2
Bill 10     3

Ho visto alcune soluzioni qui ma ho provato la maggior parte di esse e nessuna di esse restituisce effettivamente il numero di riga.

Ho provato questo:

SELECT position, username, score
FROM (SELECT @row := @row + 1 AS position, username, score 
       FROM league_girl GROUP BY username ORDER BY score DESC) 

Come derivato

... ma non sembra restituire la posizione della riga.

Qualche idea?


la riga è un nome archiviato o vuoi ordinare per chiave primaria?
Sarfraz

In SQL, i numeri di riga non sono davvero importanti. Quello che dovresti fare è aggiungere una chiave primaria di incremento automatico alla tua tabella.
simendsjo

9
La chiave primaria non deve MAI essere un identificatore di riga poiché non sono affidabili per la posizione effettiva della riga.
TheBounder

3
Inoltre, poiché il numero di riga sarebbe una funzione del punteggio che presumo non sia un valore statico, renderlo un valore auto incrementato (chiave primaria o meno) non darebbe il risultato desiderato.
kasperjj

potresti voler salvare il brutto per una funzione personalizzata, vedi datamakessense.com/mysql-rownum-row-number-function
AdrianBR

Risposte:


174

Potresti provare quanto segue:

SELECT  l.position, 
        l.username, 
        l.score,
        @curRow := @curRow + 1 AS row_number
FROM    league_girl l
JOIN    (SELECT @curRow := 0) r;

La JOIN (SELECT @curRow := 0)parte consente l'inizializzazione della variabile senza richiedere un SETcomando separato .

Scenario di test:

CREATE TABLE league_girl (position int, username varchar(10), score int);
INSERT INTO league_girl VALUES (1, 'a', 10);
INSERT INTO league_girl VALUES (2, 'b', 25);
INSERT INTO league_girl VALUES (3, 'c', 75);
INSERT INTO league_girl VALUES (4, 'd', 25);
INSERT INTO league_girl VALUES (5, 'e', 55);
INSERT INTO league_girl VALUES (6, 'f', 80);
INSERT INTO league_girl VALUES (7, 'g', 15);

Query di prova:

SELECT  l.position, 
        l.username, 
        l.score,
        @curRow := @curRow + 1 AS row_number
FROM    league_girl l
JOIN    (SELECT @curRow := 0) r
WHERE   l.score > 50;

Risultato:

+----------+----------+-------+------------+
| position | username | score | row_number |
+----------+----------+-------+------------+
|        3 | c        |    75 |          1 |
|        5 | e        |    55 |          2 |
|        6 | f        |    80 |          3 |
+----------+----------+-------+------------+
3 rows in set (0.00 sec)

19
Puoi anche inizializzare @curRowsostituendo l' JOINistruzione con una virgola, in questo modo:FROM league_girl l, (SELECT @curRow := 0) r
Mike

2
@ Mike: è vero. E probabilmente è anche più ordinato. Grazie per aver condiviso questo suggerimento.
Daniel Vassallo

2
@smhnaji MySQL richiede che a ogni "tabella derivata" venga assegnato un nome. Ho deciso di chiamarlo "r" :) ... Ha poco scopo in questo caso, ma normalmente lo useresti per fare riferimento agli attributi della tabella derivata, come se fosse una tabella reale.
Daniel Vassallo

20
Le persone dovrebbero essere consapevoli del fatto che questo numero di riga viene calcolato prima che avvenga qualsiasi ordine, quindi i numeri potrebbero essere confusi se l'ordine cambia l'ordine delle righe.
Grim ...

7
C'è un modo per ottenere questo numero di riga calcolato dopo ORDER BY?
Pierre de LESPINAY

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

c'è un modo per farlo in modo che la colonna dell'iteratore sia un numero intero e non un decimale?
kraftydevil

7

Ecco la struttura del template che ho usato:

  select
          /*this is a row number counter*/
          ( select @rownum := @rownum + 1 from ( select @rownum := 0 ) d2 ) 
          as rownumber,
          d3.*
  from 
  ( select d1.* from table_name d1 ) d3

Ed ecco il mio codice di lavoro:

select     
           ( select @rownum := @rownum + 1 from ( select @rownum := 0 ) d2 ) 
           as rownumber,
           d3.*
from
(   select     year( d1.date ), month( d1.date ), count( d1.id )
    from       maindatabase d1
    where      ( ( d1.date >= '2013-01-01' ) and ( d1.date <= '2014-12-31' ) )
    group by   YEAR( d1.date ), MONTH( d1.date ) ) d3

perfetto, funziona come charm e template può essere riutilizzato con subquery come parametro ..
Zavael

Questa soluzione funziona anche se la query di base utilizza GROUP BY
Dave R

4

Puoi anche usare

SELECT @curRow := ifnull(@curRow,0) + 1 Row, ...

per inizializzare la variabile counter.


3
@curRowpotrebbe ancora avere un valore dall'ultima volta che hai eseguito questa query nella sessione corrente.
Bill Karwin

Vero, ma solo se stai ripetendo la query sulla stessa istanza di connessione. Le variabili locali vengono eliminate automaticamente una volta chiusa la connessione.
Focolare

Sì, è quello che intendevo quando ho detto "nella sessione corrente".
Bill Karwin

3

Supponendo che MySQL lo supporti, puoi farlo facilmente con una sottoquery SQL standard:

select 
    (count(*) from league_girl l1 where l2.score > l1.score and l1.id <> l2.id) as position,
    username,
    score
from league_girl l2
order by score;

Per grandi quantità di risultati visualizzati, questo sarà un po 'lento e dovrai invece passare a un auto join.


3

Se desideri solo conoscere la posizione di un utente specifico dopo l'ordine in base al punteggio del campo, puoi semplicemente selezionare tutte le righe della tabella in cui il punteggio del campo è superiore al punteggio dell'utente corrente. E usa il numero di riga restituito + 1 per sapere quale posizione di questo utente corrente.

Supponendo che la tua tabella sia league_girle il tuo campo principale lo sia id, puoi usare questo:

SELECT count(id) + 1 as rank from league_girl where score > <your_user_score>

0

Ho trovato la risposta originale incredibilmente utile, ma volevo anche prendere un certo insieme di righe in base ai numeri di riga che stavo inserendo. Pertanto, ho racchiuso l'intera risposta originale in una sottoquery in modo da poter fare riferimento al numero di riga che stavo inserendo.

SELECT * FROM 
( 
    SELECT *, @curRow := @curRow + 1 AS "row_number"
    FROM db.tableName, (SELECT @curRow := 0) r
) as temp
WHERE temp.row_number BETWEEN 1 and 10;

Avere una sottoquery in una sottoquery non è molto efficiente, quindi varrebbe la pena verificare se ottieni un risultato migliore facendo in modo che il tuo server SQL gestisca questa query o recuperando l'intera tabella e facendo in modo che l'applicazione / il server web manipoli le righe dopo il fatto .

Personalmente il mio server SQL non è eccessivamente occupato, quindi era preferibile che gestisse le sottoquery annidate.


0

So che l'OP sta chiedendo una mysqlrisposta ma poiché ho scoperto che le altre risposte non funzionano per me,

  • La maggior parte di loro fallisce con order by
  • Oppure sono semplicemente molto inefficienti e rendono la query molto lenta per una tabella grassa

Quindi, per risparmiare tempo ad altri come me, basta indicizzare la riga dopo averli recuperati dal database

esempio in PHP:

$users = UserRepository::loadAllUsersAndSortByScore();

foreach($users as $index=>&$user){
    $user['rank'] = $index+1;
}

esempio in PHP utilizzando offset e limite per la paginazione:

$limit = 20; //page size
$offset = 3; //page number

$users = UserRepository::loadAllUsersAndSortByScore();

foreach($users as $index=>&$user){
    $user['rank'] = $index+1+($limit*($offset-1));
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.