2013-08-30 4 views
5

Ho una tabella comeCome ricalcolare un campo in GROUP

f1|f2|fk 
1 | 0|100 
1 | 1|200 
1 | 2|300 
1 | 3|400 
2 | 0|300 
2 | 1|400 
2 | 2|500 

Dove (f1,f2) è il PK e FK è una chiave esterna. Non ci sono molti valori per fk, circa 20. f2 non è mai più di 3. Ci sono molte coppie (f1,f2), tuttavia. (f1,fk) è UNIQUE.

Ora, diciamo che stiamo mappando da 100 e da 200 a A, da 300 a da 400 a B, da 500 a C: questa mappatura è un dato. Vorremmo (f1_new,fk_mapped) rimanere UNIQUE. Questo è finora risolto approssimativamente da

INSERT INTO mapped (f1_new,f2_new,fk_new) 
SELECT f1, MIN(f2), CASE fk WHEN 100 THEN A WHEN 200 THEN A END AS fk 
FROM origin 
GROUP BY f1, fk 

Ora, il problema è che occorre mantenere valori f2 0, 1, 2 della tabella mappata, quindi questo è il risultato desiderato:

f1_new|f2_new|fk_mapped 
1  | 0 |A 
1  | 1 |B 
2  | 0 |B 
2  | 1 |C 

Mi piacerebbe davvero mantenerlo in MySQL.

+1

Sono confuso - Tu dici che vuoi tenere tutti e tre i valori di 'f2' nella tabella mappata, ma il risultato desiderato si show non ha istanze dove 'f2 == 2'? – Air

+1

Vuole assegnare nuovi valori di 'f2', quindi vengono mantenuti nell'ordinazione 0, 1, 2, ... per un particolare valore di' f1'. È una colonna per rango per-f1. – Barmar

+0

Ah. Fatto. Immagino che la quarta riga nella prima tabella di esempio dovrebbe avere 'f2 == 3', quindi. Soprattutto se '(f1, f2)' è la chiave primaria. – Air

risposta

3

penso che questo dovrebbe farlo:

INSERT INTO mapped (f1_new, f2_new, fk_new) 
SELECT f1_new, f2_new, fk_new from (
    SELECT f1_new, @f2 := if(f1_new = @prev_f1, @f2+1, 0) f2_new, fk_new, @prev_f1 := f1_new 
    FROM (select f1 AS f1_new, CASE WHEN fk IN (100, 200) THEN 'A' 
            WHEN fk in (300, 400) THEN 'B' 
            WHEN fk = 500 THEN 'C' 
           END AS fk_new 
      FROM origin 
      GROUP BY f1_new, fk_new 
      ORDER BY f1_new, fk_new) new, 
     (SELECT @f2 := NULL, @prev_f1 := NULL) vars 
    ) x 

FIDDLE

+0

Credo che quanto sopra sia ottimo ma "non prevedibile" non puoi sapere se @ prev_f1: = spara prima. Ma '@ f2: = if (f1_new = @ prev_f1, @ f2 + 1, 0) + LEAST (0, @ prev_f1: = f1_new)' dovrebbe funzionare perché '+' è da sinistra a destra. Idea da [Come ottimizzare i dati di classifica in MySQL] (http://www.oreillynet.com/pub/a/mysql/2007/03/01/optimize-mysql-rank-data.html) – chx

+1

Inoltre, per [manuale ] (http://dev.mysql.com/doc/refman/5.0/en/select.html): se si utilizza GROUP BY, le righe di output vengono ordinate in base alle colonne GROUP BY come se si disponesse di un ORDER BY per stesse colonne – chx

+0

@chx Ho visto e utilizzato varianti di questo un numero di volte. Anche se potrebbe non essere garantito, in pratica penso che funzioni sempre da sinistra a destra. – Barmar