2012-10-04 1 views
6

Utilizzo delle variabili utente per numero righe

spesso mi trovo risposte qui su SO che suggerisce l'uso di variabili utente al numero qualche cosa o altro. Forse l'esempio più chiaro sarebbe una query per selezionare ogni seconda riga da un determinato set di risultati. (Questa domanda e la domanda sono simili a this answer, ma era la risposta this che ha effettivamente attivato questa domanda qui).garanzie quando si utilizzano variabili utente alle righe numero

SELECT * 
FROM (SELECT *, (@row := @row + 1) AS rownum 
     FROM (SELECT @row := 0) AS init, tablename 
     ORDER BY tablename.ordercol 
    ) sub 
WHERE rownum % 2 = 1 

Questo approccio sembra funzionare normalmente.

motivi per stare attenti

D'altra parte, il MySQ docs Stato:

Come regola generale, non si dovrebbe mai assegnare un valore a una variabile utente e leggere il valore all'interno della stessa istruzione . Potresti ottenere i risultati che ti aspetti, ma questo non è garantito. L'ordine di valutazione per le espressioni che coinvolgono variabili utente non è definito e può variare in base agli elementi contenuti in una determinata istruzione; inoltre, questo ordine non è garantito essere lo stesso tra le versioni di MySQL Server.

domanda Nucleo

Quindi la mia domanda non è come realizzare un tale ordinamento utilizzando server attuali, ma invece se la soluzione suggerita utilizzando le variabili utente è garantito a lavorare in tutte le circostanze (ragionevoli) e per tutte le versioni future di MySQL.

Per "garanzie" Intendo fonti autorevoli come la documentazione di MySQL o alcune attestazioni di conformità standard di MySQL con. In mancanza di risposte autorevoli, potrebbero essere citate altre fonti come tutorial spesso usati o parti del codice sorgente MySQL. Per "funziona" Intendo il fatto che le assegnazioni verranno eseguite in sequenza, una volta per riga del risultato e nell'ordine indotto dalla riga ORDER BY.

Esempio di una query di rottura

Per fare un esempio quanto facilmente le cose non riescono:

SELECT * 
FROM (SELECT *, (@row := @row + 1) AS rownum 
     FROM (SELECT @row := 0) AS init, tablename 
     HAVING rownum > 0 
     ORDER BY tablename.ordercol 
    ) sub 
WHERE rownum % 2 = 1 

produrrà un risultato vuoto sulla MySQL 5.5.27 attualmente installato sul SQL Fiddle. Il motivo sembra essere che la condizione HAVING fa sì che l'espressione rownum venga valutata due volte, quindi il risultato finale avrà solo numeri pari. Ho un'idea di cosa succede dietro le quinte e non sto affermando che la query con lo HAVING abbia molto senso. Voglio solo dimostrare che esiste una linea sottile tra il codice che funziona e il codice che sembra molto simile ma si rompe.

+0

@hvd, grazie per il [puntatore] (http://bugs.mysql.com/bug.php?id=35893) alla richiesta della funzione 'ROW_NUMBER'. [Dems] (http://stackoverflow.com/users/53341/dems) recentemente [mi ha detto] (http://stackoverflow.com/a/12727443) su questa funzione. Sembra abbastanza potente e ben definito per l'avvio. Peccato, sembra che ci siano pochi progressi su tale richiesta dal 2008. – MvG

+0

Ho cancellato il mio commento quando ho visto che "ROW_NUMBER" era già menzionato nei commenti e nelle risposte alla tua domanda precedente :) – hvd

+0

[Hai letto questo articolo già?] (http://www.xaprb.com/blog/2006/12/15/advanced-mysql-user-variable-techniques/) In particolare la parte 'MySQL non valuta espressioni contenenti variabili utente fino a quando non vengono inviate al client, ' –

risposta

9

Hai letto male l'affermazione. Si riferisce all'ordine delle espressioni nell'elenco SELECT, quando si utilizzano più variabili.
Come presentato, l'ordine BY su questa istruzione a variabile singola ha un ordine garantito fino alla versione corrente di MySQL e nulla in quel testo suggerisce che cambierà.

Ma il garantisce il futuro? Chissà.


Per quanto riguarda l'interrogazione rottura, hai ancora una volta frainteso come funziona MySQL. Analizziamo la tua query. Prendere atto di questa dichiarazione nel manuale

In una dichiarazione SELECT, ogni selezionare espressione viene valutata solo quando inviato al client. Ciò significa che in un HAVING, GROUP BY o ORDER BY clausola, facendo riferimento a una variabile che viene assegnato un valore nell'elenco Seleziona espressione non funziona come previsto

l'ordine di elaborazione delle query è approssimativamente

FROM/JOIN 
WHERE/ON 
GROUP BY/ROLLUP 
HAVING 
UNION 
SELECT 
ORDER BY 
@variable resolution 

Vostri criteri di "spezzato" tenta di utilizzare la variabile all'interno dello stesso livello, che si tratta solo di peccaminoso come usare un WHERE/HAVING contro un alias di colonna. Ecco perché non vedrai mai le soluzioni di numero di riga basate su variabili MySQL che utilizzano la variabile sullo stesso livello di query, è sempre in una sottoquery. La query esterna può essere considerata la client della query interna in cui è stata eseguita la rappresentazione della variabile/segnaposto. Con la tua argomentazione, puoi facilmente romperlo usando una clausola WHERE che riguarda direttamente @row (sì, eseguirà!).

+0

Non sono convinto, ma anche se ho letto la dichiarazione, ciò lascia comunque senza garanzie esplicite, e la mia domanda rimane. Quando scrivi che le versioni correnti garantiscono l'ordine, hai qualche riferimento a questo, o è solo un'esperienza personale? – MvG

+0

Oltre all'esperienza personale, considera il fatto che la soluzione sia ben nota e spesso citata. Trovami qualsiasi riferimento o feedback che non abbia funzionato in ogni momento. Questo è abbastanza buono per me. – RichardTheKiwi

+0

[Questa risposta] (http://stackoverflow.com/a/12171808) non ha funzionato per l'OP di tale domanda, in base ai commenti e a una domanda [followup] (http://stackoverflow.com/q/12243779). Fino ad ora, personalmente ritengo che questo problema sia la causa più probabile. – MvG