2010-03-18 13 views
5

ho uno schema di database che è simile al seguente:consiglio algoritmo per trovare articoli massimo entro un periodo di tempo

| 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 

vorrei produrre una query che restituisce il numero massimo di volte che un utente esegua alcune azione entro un periodo di tempo. Ad esempio, dato un periodo di tempo di 5 giorni, qual è il numero massimo di volte in cui l'utente ha calpestato il cane?

La soluzione più ovvia sarebbe iniziare in un punto zero e andare avanti di ogni giorno, riassumendo periodi di 5 giorni lungo la strada, quindi prendendo il totale massimo tra tutte le finestre di 5 giorni. tuttavia l'approccio sembra incredibilmente costoso.

Apprezzerei tutti i suggerimenti si possono avere.

EDIT 1:

Grazie per i commenti/risposte. Per rispondere: - sto usando mySQL v5.0 - Ci potrebbe essere un qualsiasi numero di eventi al giorno (per un periodo di tempo davvero) - @Paulo Santos: grazie, ma, come il commento sottolinea, ho bisogno di trovare la finestra che produce il maggior numero di risultati, la finestra stessa può scorrere. - @Mark: sembra una soluzione interessante, anche se ricordo di aver letto che mySQL non supporta il backup o il salto avanti dei cursori.
- @orbMan: sembra promettente. Non lo capisco ancora del tutto, ma ci proveremo stasera. - @mjv: un'altra soluzione promettente. guarda anche complicato, ma darò un altro sguardo

grazie ancora!

+1

Si sta utilizzando un DBMS che supporta SQL o è una fonte di file flat o qualcosa del genere, che non ha un linguaggio di query? – AxelEckenberger

+0

Può esserci solo zero o un evento al giorno? –

risposta

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 

uscita:

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

Aggiornato con una versione più permissiva che inizia a contare i giorni da qualsiasi giorno dell'evento. – RedFilter

3

Per voi specifica richiesta farei qualcosa di simile:

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

Poi tornerà il numero di tempo ogni utente ha eseguito ogni attività all'interno della specificato (@d1 e @d2) lasso di tempo.

+1

Non penso che sia proprio quello che chiede Darren. Piuttosto, vuole trovare l'intervallo di cinque giorni in cui l'utente ha eseguito l'attività più spesso. Chiede se c'è un modo per determinare ciò senza eseguire la query per * ogni * serie di cinque giorni contigui. – Callahad

+0

Darò un'occhiata a questo ... Non è banale, ma non è nemmeno così difficile. –

1

Ecco un algoritmo alternativo che si basa cursore.

Inizia con due cursori, iniziano e terminano, sia che punta alla fila iniziale, e conteggio corrente = 0, e la massima corrente = 0.

Se date_diff (end.date, begin.date) è più 5, far avanzare il cursore iniziale di una riga. Sottrarre uno dal conteggio corrente se la vecchia riga era "calpestato".

Se date_diff (end.date, begin.date) non è più di 5, avanzare il cursore fine di una riga. Uno su un conteggio corrente se la nuova riga è 'cammina il cane'. Se il conteggio corrente è maggiore del massimo corrente, imposta il massimo corrente sul conteggio corrente.

continuare fino a quando non hanno coperto tutte le righe della gamma.

1

Il seguente codice SQL risolve il problema in modo dichiarativo anziché in modo puramente procedurale/algoritmico. A seconda della situazione, è probabilmente più efficiente (rispetto al recupero dei dati [ordinati] da SQL e quindi all'esecuzione di alcuni algoritmi, e persino rispetto alle soluzioni basate su server, basate su cursore.)

L'idea è di ottenere il conteggio di eventi [rilevanti/filtrati], per utente, al giorno in una tabella separata o CTE. e quindi per ogni giorno + utente, per calcolare il numero di eventi per questo giorno e per i successivi 4 giorni, e infine per selezionare (per utente) la riga con il massimo di questi conteggi.

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 
) 

Note:
- Si usa una tabella temporanea, anche se un Common Table Expression (CTE) potrebbe essere utilizzato invece base all'host SQL sottostante.
- Il nome/sintassi particolare per la funzione DateAdd() può variare tra le implementazioni SQL.
- Anche questo implica che il campo "data" contiene "solo" una data, cioè una data o datetime/smalldatetime in cui la parte temporale è fissa (per dire 00:00). Se così non fosse, cioè se il database avesse data e ora nella colonna, questo potrebbe essere corretto a livello della query CTE/temp-table.

Problemi correlati