2010-03-18 6 views
5

Tengo un esquema de base de datos que es similar al siguiente:consejos algoritmo para la búsqueda de elementos máximos dentro de un período de tiempo

| User | Event   | Date 
|--------|---------------|------ 
| 111 | Walked dog | 2009-10-1 
| 222 | Walked dog | 2009-10-2 
| 333 | Fed Fish  | 2009-10-5 
| 222 | Did Laundry | 2009-10-6 
| 111 | Fed Fish  | 2009-10-7 
| 111 | Walked dog | 2009-10-18 
| 222 | Walked dog | 2009-10-19 
| 111 | Fed Fish  | 2009-10-21 

me gustaría producir una consulta que devuelve el número máximo de veces que un usuario realiza alguna acción dentro de un período de tiempo. Por ejemplo, dado un período de tiempo de 5 días, ¿cuál es la cantidad máxima de veces que el usuario 111 caminó al perro?

La solución más obvia sería comenzar en un punto cero y avanzar cada día, sumando periodos de 5 días en el camino, y luego sacando el total máximo de todas las ventanas de 5 días. el enfoque parece increíblemente costoso sin embargo.

Agradecería cualquier sugerencia que pueda tener.

EDIT 1:

Gracias por los comentarios/respuestas. Para responder: - Estoy usando mySQL v5.0 - Puede haber cualquier cantidad de eventos por día (por cualquier período de tiempo realmente) - @Paulo Santos: gracias, pero al igual que el comentario señala, tengo que encontrar la ventana que produce la mayoría de los resultados, la ventana en sí misma puede deslizarse. - @Mark: parece una solución interesante, aunque recuerdo haber leído que mySQL no admite el respaldo o el salto de cursores.
- @orbMan: esto parece prometedor. Todavía no lo entiendo completamente, pero intentaré esto esta noche. - @mjv: otra solución prometedora. también parece complicado, pero le daré otra apariencia

gracias de nuevo!

+1

¿Está usando un DBMS que soporta SQL o es un origen de archivo plano o algo por el estilo, que no tiene un lenguaje de consulta? – AxelEckenberger

+0

¿Solo puede haber cero o un evento por día? –

Respuesta

2
select top 1 x.Date as StartDate, DATEADD(day, 5, x.Date) as EndDate, COUNT(*) as Count 
from Event e 
inner join Event x on 1=1 
where e.Date between x.Date and DATEADD(day, 5, x.Date) 
    and e.Event = 'Walked dog' 
group by x.Date, DATEADD(day, 5, x.Date) 
order by Count desc 

Salida:

StartDate EndDate Count 
---------- ---------- ----------- 
2009-10-01 2009-10-06 2 
+0

Actualizado con una versión más permisiva que comienza a contar los días desde cualquier día del evento. – RedFilter

3

Para que específica petición que me gustaría hacer algo como:

SELECT User, Event, Count(*) 
    FROM Table 
WHERE Date between @d1 and @d2 
Group by User, Event 

Luego se devolverá el número de veces que cada usuario lleva a cabo cada tarea dentro del marco de tiempo (@d1 y @d2) especificado.

+1

No creo que eso sea exactamente lo que Darren está preguntando. Más bien, quiere encontrar el lapso de cinco días en el que el usuario realizó la tarea con más frecuencia. Él pregunta si hay una manera de determinar esto sin ejecutar su consulta para * cada * conjunto de cinco días contiguos. – Callahad

+0

Lo echaré un vistazo ... No es trivial, pero tampoco es tan difícil. –

1

Aquí hay un algoritmo alternativo que se basa en el cursor.

de inicio con dos cursores, comienzan y terminan, tanto apunta a la fila inicial, y el recuento actual = 0, y la corriente máxima = 0.

Si date_diff (end.date, begin.date) es más de 5, avanza el cursor de inicio una fila. Reste uno del recuento actual si la fila anterior fue 'caminó el perro'.

Si DATE_DIFF (end.date, begin.date) no es más de 5, avance el cursor final una fila. Aadd uno al conteo actual si la nueva fila es 'caminó el perro'. Si el recuento actual es mayor que el máximo actual, configure el recuento actual máximo al recuento actual.

Continúe hasta que haya cubierto todas las filas del rango.

1

El siguiente código SQL resuelve el problema de manera declarativa, en lugar de una forma puramente procedimental/algorítmica. Dependiendo de la situación, es probablemente más eficiente (en comparación con obtener los datos [ordenados] de SQL y luego ejecutar algún algoritmo, e incluso compararlos con las soluciones basadas en el cursor del lado del servidor).)

La idea es obtener los recuentos de eventos [relevantes/filtrados], por usuario, por día en una tabla separada o CTE. y luego para cada Día + Usuario, para contar el número de eventos para este día y para los próximos 4 días, y, finalmente, para seleccionar (por usuario) la fila con el máximo de estos recuentos.

SELECT User, Date, COUNT(*) AS EventCount 
INTO tmpTableByUsrByDay 
FROM myTable 
-- WHERE Event = some_targeted_event --Optional condition(s) 
GROUP BY User, Date, COUNT(*) 


SELECT DISTINCT User, Date AS FirstDay, 
    MAX(FiveFaysEventCount) AS EventCountForThisAndNext4Days. 
FROM (
    SELECT T1.User, T1.Date, SUM(T2.EventCount) FiveDaysEventCount 
    FROM tmpTableByUsrByDay T1 
    JOIN tmpTableByUsrByDay T2 ON T2.Date >= T1.Date 
     AND T2.Date <= DATEADD(day, 4, T1.Date) 
    GROUP BY T1.User, T1.Date 
) 

Notas:
- Se utiliza una tabla temporal, a pesar de una expresión de tabla común (CTE) podría ser utilizado en lugar dependiendo del huésped SQL subyacente.
- El nombre/sintaxis particular para la función DateAdd() puede variar entre las implementaciones de SQL.
- También esto implica que el campo "fecha" contiene "solo" una fecha, es decir, una fecha o fecha/hora pequeña donde la parte de hora es fija (por ejemplo, 00:00). Si ese no era el caso, es decir, si la base de datos tenía fecha y hora en la columna, podría corregirse al nivel de la consulta CTE/temp-table.

Cuestiones relacionadas