2010-03-18 9 views
5

ich ein Datenbankschema haben, die der folgenden ähnelt:Algorithmus Beratung für maximale Elemente innerhalb eines Zeitraums zu finden

| 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 

Ich möchte eine Abfrage erzeugen, die die maximale Anzahl der zurückgibt ein Benutzer einige führt Aktion innerhalb eines Zeitraums. Zum Beispiel, wie lange ist die maximale Anzahl von Malen, die Benutzer 111 den Hund gegangen ist, bei einer Zeitspanne von 5 Tagen? Die naheliegendste Lösung wäre, an einem beliebigen Nullpunkt zu beginnen und jeden Tag vorwärts zu gehen, wobei 5 Tage auf dem Weg summiert werden und dann die maximale Summe aller 5-Tage-Fenster genommen wird. Der Ansatz erscheint jedoch unglaublich teuer.

Ich würde mich über Ihre Vorschläge freuen.

EDIT 1:

Danke für die Kommentare/Antworten. Um zu antworten: - Ich benutze mySQL v5.0 - Es könnte eine beliebige Anzahl von Ereignissen pro Tag (pro Zeitraum wirklich) - @Paulo Santos: Danke, aber wie der Kommentar darauf hinweist, muss ich finden das Fenster, das die meisten Ergebnisse liefert, kann das Fenster selbst gleiten. - @Mark: Das sieht nach einer interessanten Lösung aus, obwohl ich mich erinnere, dass mySQL das Sichern oder Springen von Cursorn nicht unterstützt.
- @orbMan: Das sieht vielversprechend aus. Ich verstehe es noch nicht ganz, aber ich werde es heute Abend versuchen. - @mjv: eine weitere vielversprechende Lösung. sieht auch kompliziert aus, aber ich werde es ein anderes Aussehen geben

danke nochmal!

+1

Verwenden Sie einen DBMS, auf dem SQL oder ist es eine Flat File Quelle oder so etwas unterstützt, die keine Abfragesprache haben? – AxelEckenberger

+0

Kann es nur 0 oder ein Ereignis pro Tag geben? –

Antwort

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 

Ausgang:

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

Aktualisiert mit einer permissiveren Version, die Tage von jedem Ereignistag aus zählt. – RedFilter

3

Für Sie spezifische Anfrage ich so etwas tun würde:

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

Dann wird es die Anzahl der Zeit zurückgeben jeder Benutzer jede Aufgabe innerhalb des angegebenen durchgeführt (@d1 und @d2) Zeitrahmen.

+1

Ich glaube nicht, dass Darren so fragt. Vielmehr möchte er die fünftägige Zeitspanne finden, in der der Benutzer die Aufgabe am häufigsten ausführt. Er fragt, ob es eine Möglichkeit gibt, dies zu bestimmen, ohne Ihre Abfrage für * jeden * Satz von fünf zusammenhängenden Tagen auszuführen. – Callahad

+0

Ich werde einen Blick darauf werfen ... Es ist nicht trivial, aber es ist auch nicht so schwierig. –

1

Hier ist ein alternativer Algorithmus, der Cursor basiert.

beginnen mit zwei Cursor, beginnen und enden, zeigen sowohl in der Anfangsreihe und aktuelle Zählung = 0 und Strommaximum = 0.

Wenn date_diff (end.date, begin.date) mehr als 5, den Anfangscursor um eine Zeile vorrücken. Subtrahieren Sie einen von der aktuellen Zählung, wenn die alte Zeile "den Hund laufen" war.

Wenn DATE_DIFF (end.date, begin.date) nicht mehr als 5 ist, den Endcursor um eine Zeile vorrücken. Zählen Sie einen zum aktuellen Zählwert, wenn die neue Zeile 'den Hund laufen' ist. Wenn die aktuelle Anzahl größer als das aktuelle Maximum ist, setzen Sie das aktuelle Maximum auf die aktuelle Anzahl.

Fahren Sie fort, bis Sie alle Zeilen in dem Bereich abgedeckt haben.

1

Der folgende SQL-Code behandelt das Problem in einer deklarativen Weise, anstatt eine rein prozedurale/algorithmische Mode. Abhängig von der Situation ist es wahrscheinlich effizienter (verglichen mit dem Abrufen der [sortierten] Daten von SQL und dann Ausführen eines Algorithmus und sogar verglichen mit serverseitigen, cursorbasierten Lösungen.)

Die Idee besteht darin, die Anzahl der [relevanten/gefilterten] Ereignisse pro Benutzer und Tag in einer separaten Tabelle oder einem CTE zu ermitteln. und dann für jeden Tag + Benutzer, um die Anzahl der Ereignisse für diesen Tag und für die nächsten 4 Tage zu berechnen und schließlich (pro Benutzer) Zeile mit dem Maximum dieser Anzahl auszuwählen.

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 
) 

Hinweise:
- Es verwendet eine temporäre Tabelle, obwohl ein allgemeiner Tabellenausdruck (CTE) verwendet stattdessen in Abhängigkeit von der zugrundeliegenden SQL-Host werden könnte.
- Der bestimmte Name/Syntax für die Funktion DateAdd() kann zwischen SQL-Implementierungen variieren.
- Dies bedeutet auch, dass das Feld "date" nur ein Datum enthält, d. H. Entweder ein Datum oder datetime/smalldatetime, wobei der Zeitteil fest ist (um 00:00 zu sagen). Wenn dies nicht der Fall wäre, d. H. Wenn die Datenbank Datum und Zeit in der Spalte hätte, könnte dies auf der Ebene der CTE/Temptabellen-Abfrage festgelegt werden.

Verwandte Themen