2011-09-12 15 views
5

Tengo un problema que me supera un poco (estoy realmente muy contento Soy un Beta) que involucra duplicados (tan GROUP BY, HAVING, COUNT), compuesto por mantener la solución dentro de las funciones estándar que vienen con SQLite. Estoy usando el módulo sqlite3 de Python.SELECCIONANDO "primero" (según lo determinado por ORDER BY) fila FROM filas casi duplicadas (según lo determinado por GROUP BY, HAVING, COUNT) dentro de SQLite

trabajadores Ejemplo tabla, las columnas:

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

(Sí, los tipos de datos de SQLite son nominales)

Mi tabla de datos, los trabajadores, en el arranque se parece a:

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 

me gustaría para habilitar, para cargar en camiones en una nueva fábrica, a todos los trabajadores que tienen combinaciones únicas entre ColA y ColB. Para aquellos duplicados (mellizos, trillizos, etc., quizás a través del Proceso de Bokanovsky) donde las combinaciones únicas de ColA y ColB tienen más de un trabajador, me gustaría seleccionar solo una de cada conjunto de duplicados. Para que el problema sea más difícil, me gustaría poder hacer una selección adicional de cada conjunto de duplicados sobre la base de UserType en alguna forma de ORDER BY. Puede que desee seleccionar el primer "duplicado" con un UserType de "Alpha" para trabajar en un problema tremendamente astuto, o ORDER BY UserType DESC, para que pueda emitir un pedido de túnicas negras para el empleado más bajo.

Puede ver que las ID 9, 10 y 13 tienen combinaciones únicas de ColA y ColB y se identifican más fácilmente. Las combinaciones 1-a, 1-b, 2-a, 2-b y 5-a, sin embargo, tienen duplicados dentro de ellas.

Mi proceso actual, tal y como está hasta ahora:

0) Todo el mundo viene con un número de identificación único. Esto se hace al nacer.

1) SET todos los trabajadores a LoadMe = 1.

UPDATE Workers 
SET LoadMe = 1 

2) Encontrar mis duplicados en base a su similitud en dos columnas (GRUPO DE 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) ESTABLECER todos mis duplicados a 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) Para cada conjunto de duplicados en mi GROUP BY, ORDER ed BY UserType, SELECT sólo uno, el primero de la lista, que tienen LoadMe SET a 1.

Esta tabla se vería así:

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, a continuación, Identificación y estallado por los GROUP BY columnas, (y finalmente espaciadas para mayor claridad) que los mismos datos podrían ser:

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 

soy confundido en el último paso y se siente como un semi-idiota sin Epsilon.Previamente había estado extrayendo los duplicados de la base de datos en el espacio del programa y trabajando dentro de Python, pero esta situación se presenta con poca frecuencia y me gustaría resolverla de manera más permanente.

Respuesta

1

Me gusta romper un problema como este un poco. El primer paso es identificar los únicos pares Cola, colB:

SELECT ColA,ColB FROM Workers GROUP BY ColA,ColB 

Ahora, para cada uno de estos pares que desea encontrar el registro más alto de prioridad. Una unión no va a funcionar porque se va a terminar con varios registros para cada par único, sino una sub consulta funcionará:

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; 

Puede cambiar la cláusula ORDER BY en la subconsulta para controlar la prioridad. LIMIT 1 asegura que solo hay un registro para cada subconsulta (de lo contrario, sqlite devolverá el último registro que coincida con la cláusula WHERE, aunque no estoy seguro de que eso esté garantizado).

El resultado de esta consulta es una lista de registros que se cargarán con ColA, ColB, id. Yo probablemente trabajar directamente a partir de ese y deshacerse de LoadMe pero si desea mantenerlo que podría hacer esto:

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; 

que borra la bandera LoadMe y luego se lo establece en 1 para cada uno de los registros devueltos por nuestra última consulta La transacción garantiza que todo esto tiene lugar o falla como un paso y nunca deja sus campos LoadMe en un estado incoherente.