2011-06-17 20 views
5

Mi empleador tiene un clúster de proceso por lotes que procesa los trabajos enviados por los usuarios. Cada trabajo por lotes consta de tres pasos: haciendo un pivot-table-ish JOIN en SQL

  1. trabajo comenzó
  2. trabajo terminado
  3. resultados reportados para el usuario

Los registros de software de administración de trabajos por lotes cuando cada uno de estos pasos suceden, y el archivo de registro consiste de una tupla con un código de identificación del empleado que envió el trabajo, qué paso ocurrió y una marca de tiempo de cuándo ocurrió. En CSV, se ve así:

ID step timestamp 
-- ------ --------- 
A start 3533 
B start 3538 
B finish 3549 
C start 3551 
A finish 3557 
B report 3559 
C finish 3602 
A report 3603 
B start 3611 
C report 3623 
B finish 3643 
B report 3657 

Y así sucesivamente.

Una característica adicional del conjunto de datos es que hay una coincidencia entre los empleados, pero no hay coincidencia entre los empleados; es decir, cada empleado tiene que esperar hasta que su trabajo actual haya informado antes de que comience su próximo trabajo. Así que cuando clasifico por fecha y limito los resultados a un solo empleado, los registros siempre aparecen en el orden "inicio", "final", "informe".

Quiero crear una tabla dinámica que agrupe cada trabajo en una sola fila. Así los datos anteriores se convierte en:

employee-ID started finished reported 
----------- ------- -------- -------- 
A   3533  3557  3603 
B   3538  3549  3559 
B   3611  3643  3657 
C   3551  3602  3623 

Así, en el SQL:

SELECT 
    log.ID AS employee-ID, 
    start.timestamp AS started, 
    finish.timestamp AS finished, 
    report.timestamp AS reported 
FROM 
    log 

    LEFT OUTER JOIN log start ON 
    log.ID = start.ID 
     AND start.step = 'start' 

    LEFT OUTER JOIN log finish ON 
    log.ID = finish.ID 
     AND finish.step = 'finish' 
     AND start.timestamp < finish.timestamp 

    LEFT OUTER JOIN log report ON 
    log.ID = report.ID 
     AND report.step = 'report' 
     AND finish.timestamp < report.timestamp 

ORDER BY employee-ID,started,finished,reported; 

hago necesita LEFT OUTER JOIN, porque yo también necesito para identificar los trabajos que se iniciaron pero no estaban terminados o informado.

Esto funciona bastante bien. Me da las filas que necesito. Pero me da muchas filas espurias, porque los JOIN coinciden con las entradas finish y report para futuros trabajos del mismo empleado además del trabajo actual. Por lo que el informe viene a buscar como:

employee-ID started finished reported 
----------- ------- -------- -------- 
A   3533  3557  3603 
B   3538  3549  3559 
B   3538  3549  3657 <-- spurious 
B   3538  3643  3657 <-- spurious 
B   3611  3643  3657 
C   3551  3602  3623 

Es fácil reconocer las filas espurias: cada trabajo se inicia sólo una vez, por lo que teniendo en cuenta la clasificación, la fila correcta es la primera fila con un único "comenzó" valor. Estoy trabajando en torno al problema de las filas espurias en este momento en el nivel de la aplicación, omitiendo las filas espurias, pero eso parece, bueno, poco elegante. Y es costoso: algunos de estos empleados tienen docenas de trabajos enviados, por lo que actualmente, los resultados de mis consultas son aproximadamente el 15% de las entradas legítimas y el 85% espurias. Eso es un montón de tiempo perdido omitiendo entradas falsas. Sería bueno si cada trabajo tuviera una identificación única, pero simplemente no tengo esa información.

Necesito limitar de alguna manera la UNIÓN para que seleccione solo una entrada "finalizada" e "informada" para cada entrada "iniciada": la única entrada que tiene la marca de tiempo mínima mayor que la marca de tiempo del paso anterior. Intenté hacer esto utilizando una subconsulta como la tabla a la que me estaba INSCRIBIENDO, pero no pude entender cómo hacerlo sin una subconsulta correlacionada. También intenté hacerlo al usar "ID de empleado de GROUP BY, comencé", pero esto no necesariamente eligió la fila "correcta". La mayoría de las filas informadas por "GROUP BY" fueron las incorrectas.

Entonces, los gurús de SQL, ¿es posible informar solo las filas que necesito? Y si es así, ¿cómo? Estoy usando sqlite3 en este momento, pero podría transferir la base de datos a MySQL si es necesario.

Respuesta

2

El problema es cómo se está uniendo a finish y report

Usted no quiere que el start.timestamp < finish.timestamp que realmente quieren start.timestamp < MIN(finish.timestamp)

Por supuesto que no funciona por lo que tendrá que hacerlo después de la unión

p. Ej.

SELECT 
    log.ID AS employee_ID, 
    start.timestamp AS started, 
    MIN(finish.timestamp) AS finished, 
    MIN(report.timestamp) AS reported 
FROM 
    log 


LEFT OUTER JOIN log start ON 
log.ID = start.ID 
    AND start.step = 'start' 

LEFT OUTER JOIN log finish ON 
log.ID = finish.ID 
    AND finish.step = 'finish' 
    AND start.timestamp < finish.timestamp 

LEFT OUTER JOIN log report ON 
log.ID = report.ID 
    AND report.step = 'report' 
    AND finish.timestamp < report.timestamp 

GROUP BY log.ID, 
    start.timestamp 
ORDER BY 
    employee_ID,started,finished,reported 

también que probablemente se podría convertir el comienzo de una combinación interna, ya que no hace un montón de sentido tener un acabado sin un comienzo

+0

que funciona perfectamente! Se ralentiza la consulta en menos del 5%, y la aplicación se ejecuta mucho más rápido ahora que no tiene que hacer el filtrado en sí. Muchas gracias. – STH