2011-09-12 5 views
5

Ho un problema che è un po 'oltre me (sono davvero terribilmente felice Sono un beta) che coinvolge duplicati (quindi GROUP BY, HAVING, COUNT), aggravato mantenendo la soluzione all'interno delle funzioni standard fornite con SQLite. Sto usando il modulo sqlite3 da Python.SELEZIONE della riga "first" (come determinato da ORDER BY) FROM delle righe quasi duplicate (come determinato da GROUP BY, HAVING, COUNT) all'interno di SQLite

lavoratori tabella di esempio, Colonne:

* ID: integer, auto-incrementing 
* ColA: integer 
* ColB: varchar(20) 
* UserType: varchar(20) 
* LoadMe: Boolean 

(Sì, i tipi di dati di SQLite sono nominali)

mia tabella di dati, operai, all'inizio si presenta come:

ID ColA ColB UserType LoadMe 
1 1  a  Alpha  0 
2 1  b  Beta  0 
3 2  a  Alpha  0 
4 2  a  Beta  0 
5 2  b  Delta  0 
6 2  b  Alpha  0 
7 1  a  Delta  0 
8 1  b  Epsilon 0 
9 1  c  Gamma  0 
10 4  b  Delta  0 
11 5  a  Alpha  0 
12 5  a  Beta  0 
13 5  b  Gamma  0 
14 5  a  Alpha  0 

vorrei per consentire, per Carico su camion in un nuovo stabilimento, tutti i lavoratori che hanno combinazioni uniche tra ColA e ColB. Per quei duplicati (gemelli, terzine, ecc., Forse tramite il processo di Bokanovsky) dove le combinazioni uniche di ColA e ColB hanno più di un lavoratore, vorrei selezionarne solo uno per ogni serie di duplicati. Per rendere il problema più difficile, vorrei inoltre essere in grado di effettuare una selezione da ciascun set di duplicati sulla base di UserType in una qualche forma di ORDER BY. Potrei voler selezionare il primo "duplicato" con un UserType di "Alpha", per lavorare su un problema spaventosamente ingegnoso, o ORDER BY UserType DESC, che io possa emettere un ordine per le tuniche nere per il più basso degli operai.

È possibile vedere che gli ID 9, 10 e 13 hanno combinazioni univoche di ColA e ColB e sono più facilmente identificabili. Le combinazioni 1-a, 1-b, 2-a, 2-b e 5-a, tuttavia, hanno duplicati al loro interno.

mio processo in corso, così com'è finora:

0) Chiunque viene fornito con un numero ID univoco. Questo è fatto alla nascita.

1) SET tutti i lavoratori a LoadMe = 1.

UPDATE Workers 
SET LoadMe = 1 

2) trovare il mio duplicati in base alla loro somiglianza in due colonne (GROUP BY Cola, ColB):

SELECT Wk1.* 
FROM Workers AS Wk1 
INNER JOIN (
    SELECT ColA, ColB 
    FROM Workers 
    GROUP BY ColA, ColB 
    HAVING COUNT(*) > 1 
) AS Wk2 
ON Wk1.ColA = Wk2.ColA 
AND Wk1.ColB = Wk2.ColB 
ORDER BY ColA, ColB 

3) Impostare tutti i miei duplicati su LoadMe = 0.

UPDATE Workers 
SET LoadMe = 0 
WHERE ID IN (
    SELECT Wk1.ID 
    FROM Workers AS Wk1 
    INNER JOIN (
     SELECT ColA, ColB 
     FROM Workers 
     GROUP BY ColA, ColB 
     HAVING COUNT(*) > 1 
    ) AS Wk2 
    ON Wk1.ColA = Wk2.ColA 
    AND Wk1.ColB = Wk2.ColB 
) 

4) Per ogni serie di duplicati nel mio GROUP BY, ORDER ed BY UserType, SELECT solo uno, il primo della lista, per avere LoadMe SET a 1.

Questa tabella sarà simile:

ID ColA ColB UserType LoadMe 
1 1  a  Alpha  1 
2 1  b  Beta  1 
3 2  a  Alpha  1 
4 2  a  Beta  0 
5 2  b  Delta  0 
6 2  b  Alpha  1 
7 1  a  Delta  0 
8 1  b  Epsilon 0 
9 1  c  Gamma  1 
10 4  b  Delta  1 
11 5  a  Alpha  1 
12 5  a  Beta  0 
13 5  b  Gamma  1 
14 5  a  Alpha  0 

ORDER ed BY Cola, ColB, UserType, poi ID, e rotto dai GROUP BY colonne, (e, infine, distanziati per chiarezza) che i dati stessi potrebbe essere simile:

ID ColA ColB UserType LoadMe 
1 1  a  Alpha  1 
7 1  a  Delta  0 

2 1  b  Beta  1 
8 1  b  Epsilon 0 

9 1  c  Gamma  1 

3 2  a  Alpha  1 
4 2  a  Beta  0 

6 2  b  Alpha  1 
5 2  b  Delta  0 

10 4  b  Delta  1 

11 5  a  Alpha  1 
14 5  a  Alpha  0 
12 5  a  Beta  0 

13 5  b  Gamma  1 

sono confuso sull'ultimo passo e si sente come un semi-deficiente Epsilon-meno.In precedenza avevo tirato i duplicati dal database nello spazio del programma e lavorando all'interno di Python, ma questa situazione non si verifica raramente e mi piacerebbe risolverlo in modo permanente.

risposta

1

Mi piace rompere un problema come questo un po '. Il primo passo è quello di individuare le uniche coppie di Cola, COLB:

SELECT ColA,ColB FROM Workers GROUP BY ColA,ColB 

Ora, per ciascuna di queste coppie che si desidera trovare il più alto record di priorità. Un join non funziona perché vi ritroverete con più record per ogni coppia unica, ma una sottoquery funziona:

SELECT ColA,ColB, 
    (SELECT id FROM Workers w1 
    WHERE w1.ColA=w2.ColA AND w1.ColB=w2.ColB 
    ORDER BY UserType LIMIT 1) AS id 
FROM Workers w2 GROUP BY ColA,ColB; 

È possibile modificare la clausola ORDER BY nella subquery per controllare la priorità. LIMIT 1 assicura che vi sia un solo record per ogni sottoquery (altrimenti sqlite restituirà l'ultimo record che corrisponde alla clausola WHERE, anche se non sono sicuro che sia garantito).

Il risultato di questa query è un elenco di record da caricare con ColA, ColB, id. Io probabilmente lavorare direttamente da questo e di sbarazzarsi di LoadMe, ma se si vuole mantenere lo si possa fare ciò:

BEGIN TRANSACTION; 
UPDATE Workers SET LoadMe=0; 
UPDATE Workers SET LoadMe=1 
WHERE id IN (SELECT 
    (SELECT id FROM Workers w1 
    WHERE w1.ColA=w2.ColA AND w1.ColB=w2.ColB 
    ORDER BY UserType LIMIT 1) AS id 
    FROM Workers w2 GROUP BY ColA,ColB); 
COMMIT; 

che cancella la bandiera LoadMe e quindi imposta a 1 per ciascuno dei record restituiti dalla nostra ultima query. La transazione garantisce che tutto ciò avvenga o fallisce in un unico passaggio e non lascia mai i campi LoadMe in uno stato incoerente.