2016-03-09 9 views
6

Ho seguito, semplice tabellaMySQL set di risultati ordinato da posizioni fisse

Item (id, name, date, fixed_position) 

(1, 'first entry', '2016-03-09 09:00:00', NULL) 
(2, 'second entry', '2016-03-09 04:00:00', 1) 
(3, 'third entry', '2016-03-09 05:00:00', NULL) 
(4, 'fourth entry', '2016-03-09 19:00:00', NULL) 
(5, 'fifth entry', '2016-03-09 13:00:00', 4) 
(6, 'sixth entry', '2016-03-09 21:00:00', 2) 

Il numero di elementi non è fisso, infatti può variare da 100 a ~ ~ 1000.

Quello che voglio ottenere è eseguire una query per restituire il set di articoli ordinati per date campo che prende in considerazione il campo fixed_position, che corrisponde a qualcosa come risultati "appuntati" a posizioni specifiche. Se fixed_position per la voce data non è NULL, il risultato deve essere bloccato in n-esima posizione e se fixed_position è NULL, il ORDER BY deve avere la precedenza.

output desiderato di query per una spiegazione più luminoso: soluzione

(2, 'second entry', '2016-03-09 04:00:00', 1) // pinned to 1-st position 
(6, 'sixth entry', '2016-03-09 21:00:00', 2)  // pinned to 2-nd position 
(3, 'third entry', '2016-03-09 05:00:00', NULL) // ORDER BY `date` 
(5, 'fifth entry', '2016-03-09 13:00:00', 4)  // pinned to 4-th position 
(1, 'first entry', '2016-03-09 09:00:00', NULL) // ORDER BY `date` 
(4, 'fourth entry', '2016-03-09 19:00:00', NULL) // ORDER BY `date` 

Ho provato ha scritto in Ordering MySql results when having fixed position for some items, ma anche con il metodo copia-incolla questo non sembra funzionare affatto.

Quello che ho provato fino a questo punto è questa query:

SELECT 
    @i := @i +1 AS iterator, 
    t.*, 
    COALESCE(t.fixed_position, @i) AS positionCalculated 
FROM 
    Item AS t, 
    (
SELECT 
    @i := 0 
) AS foo 
GROUP BY 
    `id` 
ORDER BY 
    positionCalculated, 
    `date` DESC 

che restituisce:

iterator | id | name  | date    | fixed_position | positionCalculated 
1   1 first entry 2016-03-09 09:00:00 NULL    1 
2   2 second entry 2016-03-09 04:00:00 1    1 
6   6 sixth entry 2016-03-09 21:00:00 2    2 
3   3 third entry 2016-03-09 05:00:00 NULL    3 
4   4 fourth entry 2016-03-09 19:00:00 NULL    4 
5   5 fifth entry 2016-03-09 13:00:00 4    4 

Vuol MySQL in grado di eseguire tale compito o dovrei prendere approccio backend ed eseguire di array_merge() su due PHP set di risultati?

+0

Qual è la SQL che si sta utilizzando? In modo che possiamo vedere ciò che hai provato. IE: 'order by date, ifnull (fixed_position, 0)' –

+0

Ho aggiunto la query di esempio nella mia domanda. Trascorso più di 5 ore in questa ricerca di una risposta, si è conclusa con quasi nulla. –

+0

È 'fixed_position' un campo nella tabella o un campo calcolato? –

risposta

3

Un metodo di forza bruta per risolvere questo sarebbe quello di creare prima una tabella conteggio avere una quantità di righe più grande della tabella originale:

SELECT @rn := @rn + 1 AS rn 
FROM (
    SELECT 1 AS x UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1) AS t1 
CROSS JOIN ( 
    SELECT 1 AS x UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1) AS t2 
CROSS JOIN (SELECT @rn := 0) AS v 

Quindi è possibile LEFT JOIN questa tabella per una tabella derivata che contiene tutti posizioni fisse della tabella originale:

SELECT Tally.rn 
FROM (
    ... tally table query here 
) AS Tally 
LEFT JOIN (
    SELECT fixed_position 
    FROM Item 
) AS t ON Tally.rn = t.fixed_position 
WHERE t.t.fixed_position IS NULL 

Quanto sopra restituisce le posizioni dell'ordine mancanti da completare.

Demo here

È ora possibile utilizzare la query di cui sopra come l'ennesimo tabella derivata uniti alla tabella originale per ottenere l'ordine desiderato:

SELECT id, name, `date`, fixed_position, Gaps.rn, 
     derived.seq, Gaps.seq 
FROM (
    SELECT id, name, `date`, fixed_position, 
     @seq1 := IF(fixed_position IS NULL, @seq1 + 1, @seq1) AS seq 
    FROM Item  
    CROSS JOIN (SELECT @seq1 := 0) AS v 
    ORDER BY `date` 
) AS derived 
LEFT JOIN ( 
    SELECT Tally.rn, 
      @seq2 := @seq2 + 1 AS seq 
    FROM (
     SELECT @rn := @rn + 1 AS rn 
     FROM (
     SELECT 1 AS x UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1) AS t1 
     CROSS JOIN ( 
     SELECT 1 AS x UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1) AS t2 
     CROSS JOIN (SELECT @rn := 0) AS v 
    ) AS Tally 
    LEFT JOIN (
     SELECT fixed_position 
     FROM Item 
    ) AS t ON Tally.rn = t.fixed_position 
    CROSS JOIN (SELECT @seq2 := 0) AS v 
    WHERE t.t.fixed_position IS NULL 
    ORDER BY rn 
) AS Gaps ON (derived.seq = Gaps.seq) AND (derived.fixed_position IS NULL) 
ORDER BY COALESCE(derived.fixed_position, Gaps.rn) 

Demo here

+0

Questo è semplicemente perfetto! Grazie, hai salvato la mia giornata e innumerevoli ore di lavoro. –

0

ho avuto lo stesso problema (ordina per data + inserisce righe con valori di posizione fissi). La soluzione sopra sembra funzionare. Ma devi sapere quanti valori ha il tuo tavolo. La linea:

SELECT 1 AS x UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1

deve ottenere esteso se si dispone di più righe perché la tabella temporanea è troppo breve. Dopo alcuni risultati di ricerca di Google e test nel mio DB, ho trovato una soluzione che si adatta alle tue esigenze ed è più intelligente da capire, credo.

SELECT * FROM (SELECT create_date, fixedposition, @rownum:[email protected] + 1 AS colposition, 1 AS majorEntry FROM myTable JOIN (SELECT @rownum:=0) r WHERE fixedposition IS NULL ORDER BY crdate DESC) AS orderedFixed UNION ALL (SELECT create_date, fixedposition, fixedposition AS colposition, 0 AS majorEntry FROM myTable WHERE fixedposition IS NOT NULL ORDER BY fixedposition ASC) ORDER BY colposition ASC, majorEntry

Quindi questo è come funziona: ci sono due istruzioni SELECT. Il primo SELECT cerca tutte le colonne senza una posizione fissa e le ordina in base alla data. Inoltre fa un JOIN per aggiungere una colonna contatore di righe. Il secondo cerca tutte le file con una posizione fissa e restituisce l'ordinamento per "colposizione".

Sia select ottiene combinati da un UNION ALL. L'unione viene prima classificata dalla colposizione ASCending e nella seconda fase dal valore majonEntry che indica che le righe con posizione fissa devono essere posizionate prima delle altre righe che hanno la stessa posizione.

Problemi correlati