Come posso fare un GROUP BY complesso in MySQL?


8

Ho una tabella che contiene diverse chiavi in ​​altre tabelle (in cui ogni chiave è composta da più colonne). Vorrei poter raggruppare le righe che hanno una chiave uguale, ma non voglio raggrupparle tutte insieme. Non è semplice GROUP BYsulla chiave, ma piuttosto voglio essere in grado di creare gruppi di dire 10. Quindi se una particolare chiave si presentasse 50 volte otterrei 5 risultati quando faccio questo raggruppamento (5 gruppi di 10). Voglio anche che questo raggruppamento avvenga casualmente all'interno della chiave.

Non conoscevo il modo diretto per farlo, e il metodo della rotonda che mi è venuto in mente non funziona come penso che dovrebbe. La soluzione rotonda che ho trovato è stata quella di creare una nuova colonna per ogni chiave che fosse un numero intero tale che il valore irappresenti il ithverificarsi di quella chiave (ma in ordine casuale). Potrei quindi fare una divisione intera in modo che ogni n (diciamo 10) righe all'interno della chiave abbia lo stesso valore, e potrei fare una GROUP BYsu quel valore.

C'è un modo più diretto per realizzare ciò che ho appena descritto? È piuttosto imbarazzante e ho riscontrato problemi nella creazione della nuova colonna dell'indice (come ho descritto in questa domanda ).

EDIT: Prima di tutto nota che questo è per MySQL. Aggiungerò un esempio nel caso in cui il mio obiettivo non fosse chiaro. I documenti MySQL mostrano un metodo per arrivarci quasi :

CREATE TABLE animals (
    grp ENUM('fish','mammal','bird') NOT NULL,
    id MEDIUMINT NOT NULL AUTO_INCREMENT,
    name CHAR(30) NOT NULL,
    PRIMARY KEY (grp,id)
) ENGINE=MyISAM;

INSERT INTO animals (grp,name) VALUES
    ('mammal','dog'),('mammal','cat'),
    ('bird','penguin'),('fish','lax'),('mammal','whale'),
    ('bird','ostrich');

SELECT * FROM animals ORDER BY grp,id;

Questo crea una tabella che, sebbene non sia ciò che voglio, si avvicina:

+--------+----+---------+
| grp    | id | name    |
+--------+----+---------+
| fish   |  1 | lax     |
| mammal |  1 | dog     |
| mammal |  2 | cat     |
| mammal |  3 | whale   |
| bird   |  1 | penguin |
| bird   |  2 | ostrich |
+--------+----+---------+

Vorrei essenzialmente GROUP BYid, tranne che vorrei che i record mammalavessero un "gruppo" per gli ID 1-10, un altro "gruppo" per gli ID 11-20, ecc. Tuttavia, lo farei con una tabella esistente, e non vorrei necessariamente che "dog" si presentasse con ID 1. Vorrei che l'ordinamento iniziale fosse casuale, ma poi deterministico da allora in poi.


I would want that initial ordering to be random, but then deterministic from then out.<- dire cosa? Penso che qualunque cosa tu faccia, dovrai mettere i record in una seconda tabella di qualche tipo. Come funziona esattamente questa logica aziendale? Poiché non c'è nulla da richiedere (ad esempio) al cane di venire prima. E cosa intendi con I would want the records from *mammal* to have one "group" for IDs 1-10, and another for IDs 11-20... puoi illustrarlo con un'altra tabella, concentrandoti sui mammiferi, nella descrizione della domanda sopra?
jcolebrand

@jcolebrand Per ogni disco che è un mammifero voglio assegnare un ID univoco da 1 a numMammal. Non mi interessa davvero quale ID dogottenga, ma non voglio che dipenda dall'ordine di inserimento originale.
Michael McGowan,

@jcolebrand Supponiamo che avessi anche una colonna di peso. Potrei voler prendere il peso medio dei mammiferi con ID da 1 a 10 e il peso medio dei mammiferi con ID da 11 a 20, ecc. Questo è il senso che voglio GROUP BY. Potrei quindi voler accoppiare gruppi di 10 per trovare la correlazione tra la media. Ho bisogno di questo ordinamento casuale perché se l'ordine di inserzione originale fosse ordinato in base al peso, questo mi darebbe i risultati sbagliati. Spero di avere un senso.
Michael McGowan,

Penso ancora che un esempio di TABELLA nella domanda sarebbe utile. Ma penso di vedere quello che vuoi. Semplicemente non vedo dove siano queste cose nel dominio di SQL, dato che non si tratta proprio di set. SQL è il dominio degli insiemi. Vorrei fare la logica che stai suggerendo in un file php con un singolo (o due) loop. SQL farebbe comunque un efficace ciclo singolo per assegnare i numeri.
jcolebrand

@jcolebrand Può darsi che non dovrei farlo in SQL, ma ho pensato che una regola empirica utile fosse lasciare che il database facesse il lavoro per te. Sto ancora imparando i limiti di ciò che dovrebbe e non dovrebbe essere elaborato all'interno del database, ma in passato, quando ho provato a estrarre i risultati, elaborarli e quindi reinserirli, ho ottenuto scarsi risultati in termini di prestazioni (ore e ore perché probabilmente stavo facendo qualcosa di sbagliato nel reinserire i risultati).
Michael McGowan,

Risposte:


5

Che ne dici di fare un po 'di matematica sulla colonna ID per generare dinamicamente il gruppo?

SELECT grp, FLOOR(id/10) AS id_grp
FROM animals
GROUP BY grp, id_grp

Questo ti darebbe gruppi di 10 in base all'ID del record. Ho usato la tabella dei tuoi animali sopra per generare i dati di seguito.

Dati di esempio

 INSERT INTO animals VALUES
 ('mammal',10,'dog'),('mammal',11,'dog'),('mammal',12,'dog'),
 ('mammal',21,'cat'),('mammal',22,'cat'),('mammal',23,'cat'),
 ('mammal',24,'cat'),('mammal',25,'cat'),('mammal',26,'cat'),
 ('bird',30,'penguin'),('bird',31,'penguin'),('bird',32,'penguin'),
 ('bird',33,'penguin'),('fish',44,'lax'),('fish',45,'lax'),
 ('fish',46,'lax'),('fish',47,'lax'),('fish',48,'lax'),
 ('mammal',31,'whale'),*'fish',51,'lax'),('fish',52,'lax'),
 ('fish',53,'lax'),('fish',54,'lax'),('bird',10,'ostrich');

Uscita query

 +--------+--------+
 | grp    | id_grp |
 +--------+--------+
 | fish   |      4 |
 | fish   |      5 |
 | mammal |      1 |
 | mammal |      2 |
 | mammal |      3 |
 | bird   |      1 |
 | bird   |      3 |
 +--------+--------+
 7 rows in set (0.00 sec)

Avevo intenzione di fare matematica simile se potessi prima generare la tabella in questione. Ho problemi a ottenere gli ID assegnati correttamente.
Michael McGowan,


@jcolebrand Grazie, sto ancora guardando il primo link. Ho provato un approccio simile al secondo link e ho avuto problemi con esso: dba.stackexchange.com/questions/1932/…
Michael McGowan,

2

In SQL generalmente questo sarebbe:

  • una sottoselezione DISTINCT
  • ISCRIVITI alla tabella principale sui tasti DISTINCT
  • NTILE con PARTITION BY sui tasti DISTINCT e un ORDER BY per creare bucket

Non è un aggregato, quindi GROUP BY non è necessario

Modificare:

In realtà, NTILE è sufficiente da solo per creare "n bucket per set di valori distinti"


Non credo che MySQL supporti NTILE.
Michael McGowan,

Spiacenti, questo link implica che lo fa. Esiste probabilmente una soluzione / soluzione alternativa per NTILE.
gbn

Ottima soluzione Oracle.
Leigh Riffel

@Leigh Riffel: e SQL Server. E Sybase. E PostGres ...
gbn

2
@gbn Non MySQL era il punto che avrei dovuto chiarire. L'articolo fa riferimento a Oracle.
Leigh Riffel

1

Non vedo ancora soluzioni complete (che funzionano davvero in MySQL), quindi questa è la soluzione che probabilmente userò:

  1. Generare gli ID casuali al di fuori di SQL (in una sorta di script)
  2. Applicare la divisione intera su quegli ID per raggrupparli di conseguenza.

Spero ancora che qualcuno possa battere questa risposta; Non voglio accettare la mia risposta. L'ho detto prima, ma sapevo fin dall'inizio come fare # 2; # 1 è ciò che mi ha turbato. Se riesci a rispondere al n. 1, allora rispondi anche a un'altra domanda , ma potrebbe essere possibile rispondere a questa domanda in qualche altro modo in modo da bypassare il n. 1.


0
-- Change 'ValueField' to whatever provides your 'group' values

set @rownum := 0;
set @groupnum := 0;
set @lastGroup := 0;

select
    ValueField, 
    Grouping, 
    count(1) as Count
from
    (
        -- We have a row number for each record
        select
            -- Set the record number
            case when @lastGroup != ValueField 
                then @rownum := 0 else (@rownum := @rownum + 1) 
            end as Record, 

            -- Determine which group we are in
            case
                -- If the 'Group' changed, reset our grouping
                when @lastGroup != ValueField 
                    then @groupnum := 0

                -- Determines the grouping value; group size is set to 10
                when floor(@rownum / 10) != @groupnum 
                    then @groupnum := @groupnum + 1 
                else @groupnum
            end as Grouping,

            -- Track the last Group
            case 
                when @lastGroup != ValueField 
                    then @lastGroup := ValueField 
                else @lastGroup 
            end as LastGroup,

            -- Value field that will be aggregated
            ValueField 
        from 
            YourTable
        order by 
            ValueField
    ) as x
group by
    ValueField, 
    Grouping;
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.