SQL, um minimale sequentielle Zugriffstage zu bestimmen?
Folgende Benutzer History-Tabelle enthält einen Datensatz für jeden Tag einen bestimmten Benutzer zugegriffen hat eine website (in einem 24-Stunden UTC-Zeit). Es hat viele Tausende von Datensätzen, aber nur einen Datensatz pro Tag pro Benutzer. Wenn der Benutzer nicht auf die website zugegriffen, die für diesen Tag kein Datensatz generiert werden.
Id UserId CreationDate ------ ------ ------------ 750997 12 2009-07-07 18:42:20.723 750998 15 2009-07-07 18:42:20.927 751000 19 2009-07-07 18:42:22.283
Was ich Suche ist eine SQL-Abfrage auf diese Tabelle mit einer guten Leistung, das mir sagt, welche Benutzer-IDS zugegriffen haben, die website für (n) Tage ohne fehlt ein Tag.
In anderen Worten, , wie viele Benutzer (n) Datensätze in dieser Tabelle sequentiell (Tag vor oder am Tag danach) Termine? Wenn jeder Tag fehlen, ist die Reihenfolge, die Sequenz ist defekt und sollte neu starten wieder bei 1; wir suchen für Benutzer, die erreicht haben, eine kontinuierliche Anzahl der Tage, die hier mit keine Lücken.
Jede ähnlichkeit zwischen dieser Abfrage und eine Besondere Stack-Überlauf Abzeichen ist rein zufällig natürlich.. 🙂
InformationsquelleAutor der Frage Jeff Atwood | 2009-07-24
Du musst angemeldet sein, um einen Kommentar abzugeben.
Offensichtlich ist die Antwort:
EDIT:
Okay, hier meine ernsthafte Antwort:
EDIT:
[Jeff Atwood] Dies ist eine große schnelle Lösung und verdient es, angenommen zu werden, aber Rob Farley ' s Lösung ist auch hervorragend und wohl auch schneller (!). Bitte überprüfen Sie es heraus zu stehen!
InformationsquelleAutor der Antwort Spencer Ruport
Wie über (und bitte stellen Sie sicher, dass die Vorherige Anweisung endet mit einem Semikolon):
Die Idee ist, dass, wenn wir haben die Liste mit den Tagen (als Zahl), und ein row_number, dann verpasste Tage machen den Versatz zwischen diesen beiden Listen etwas größer. So sind wir auf der Suche für einen Bereich mit einem gleichmäßigen Aufmaß.
Könnten Sie "ORDER BY NumConsecutiveDays DESC" am Ende dieses, oder sagen Sie "count(*) > 14" für einen Schwellenwert...
Ich noch nicht getestet, aber einfach nur zu schreiben es aus der Spitze von meinem Kopf. Hoffentlich funktioniert in SQL2005 und auf.
...und wäre sehr geholfen, indem Sie einen index auf tablename(Benutzer-id, ERSTELLUNGSDATUM)
Bearbeitet werden: Stellt sich heraus, Offset ist ein reserviertes Wort, so habe ich TheOffset statt.
Bearbeitet werden: Der Vorschlag, verwenden Sie COUNT(*) ist sehr gültig - sollte ich das getan haben, in Erster Linie aber war nicht wirklich zu denken. Zuvor war es mit datediff(day, min(CreationDate), max(CreationDate)) statt.
Rob
InformationsquelleAutor der Antwort Rob Farley
Wenn Sie ändern, können Sie das schema für die Tabelle, ich würde vorschlagen, das hinzufügen einer Spalte
LongestStreak
auf die Tabelle, die Sie würde auf der Anzahl von aufeinander folgenden Tagen Ende desCreationDate
. Es ist einfach, um die Tabelle zu aktualisieren nach der Anmeldung (ähnlich zu dem, was Sie tun bereits, wenn keine Zeilen vorhanden sind, wird der aktuelle Tag, Sie werde prüfen, ob eine Zeile existiert für den vorherigen Tag. Wenn true, werden Sie erhöhen denLongestStreak
in die neue Zeile, ansonsten werden Sie auf 1 setzen.)Die Abfrage wird offensichtlich sein, nachdem Sie diese Spalte:
InformationsquelleAutor der Antwort Mehrdad Afshari
Einige schön ausdrucksstark SQL entlang der Linien von:
Vorausgesetzt, Sie haben eine benutzerdefinierte Aggregatfunktion etwas entlang der Linien von (Achtung dies ist buggy):
InformationsquelleAutor der Antwort Joshuamck
Scheint, wie könnte man sich die Tatsache zunutze, dass kontinuierlich über n Tage erfordern würde, es werden n Zeilen.
So etwas wie:
InformationsquelleAutor der Antwort Bill
Tun dies mit einer einzigen SQL-Abfrage scheint zu kompliziert für mich. Lassen Sie mich brechen diese Antwort nach unten in zwei Teile.
Führen Sie einen täglichen cron-job, der überprüft, ob jeder Benutzer, ob er hat sich heute eingeloggt und dann erhöht einen Zähler, wenn er oder setzt ihn auf 0, wenn er noch nicht.
- Exportieren Sie diese Tabelle auf einem server, der nicht läuft, Ihre website und wird nicht mehr benötigt für eine Weile. 😉
- Sortieren nach Benutzer, dann Datum.
- durch Sie gehen nacheinander, halten Sie einen Zähler...
InformationsquelleAutor der Antwort Kim Stebel
Wenn das so wichtig für Sie, die Quelle dieses Ereignis und fahren eine Tabelle geben Sie diese info. Keine Notwendigkeit zu töten, die Maschine mit all diesen verrückten Abfragen.
InformationsquelleAutor der Antwort
Könnte man eine rekursive CTE (SQL Server 2005+):
InformationsquelleAutor der Antwort OMG Ponies
Joe Celko hat ein komplettes Kapitel auf diese in SQL for Smarties (Aufruf: es Läuft und Sequenzen). Ich habe nicht das Buch zu Hause, so dass, wenn ich auf Arbeit... werde ich tatsächlich diese Antwort. (vorausgesetzt, die Geschichte Tabelle namens dbo.UserHistory und die Anzahl der Tage ist @Tage)
Anderen führen, ist aus SQL Team-das blog läuft
Andere Idee, die ich hatte, aber nicht über eine SQL-server-praktische arbeiten ist hier die Verwendung einer CTE mit einer partitionierten ROW_NUMBER wie diese:
Den oben genannten ist wahrscheinlich HÄRTER, als es sein muss, aber Links, wie ein Gehirn kitzeln, wenn Sie haben eine andere definition von "führen" als nur Termine.
InformationsquelleAutor der Antwort IDisposable
Ein paar SQL Server 2012 Optionen (vorausgesetzt, N=100 unten).
Aber mit meinem Beispiel-Daten im folgenden herausgearbeitet effizienter
Beide berufen sich auf die Einschränkung erwähnt in der Frage, dass es bei den meisten ein Datensatz pro Tag pro Benutzer.
InformationsquelleAutor der Antwort Martin Smith
Sowas?
InformationsquelleAutor der Antwort John Nilsson
Habe ich eine einfache mathematische Eigenschaft zu identifizieren, die nacheinander auf die Website zugreifen. Diese Eigenschaft ist, dass Sie den Tag Unterschied zwischen dem ersten Zugriff und dem letzten mal gleich der Anzahl der Datensätze in der access-Tabelle anmelden.
Hier sind SQL-Skript, das ich getestet habe in Oracle DB (sollte es in anderen DBs):
Tabelle prep-Skript:
InformationsquelleAutor der Antwort Dilshod Tadjibaev
Die Aussage
cast(convert(char(11), @startdate, 113) as datetime)
entfernt die Zeit, die Teil der Laufenden, damit wir starten um Mitternacht.Ich würde auch vermuten, dass die
creationdate
unduserid
Spalten indiziert sind.Ich erkannte, dass dies nicht allen erzählen, die Benutzer und Ihre total aufeinander folgenden Tagen. Aber wird Ihnen sagen, welche Benutzer wurde der Besuch einer festgelegten Anzahl von Tagen von einem Datum Ihrer Wahl.
Überarbeitet Lösung:
Habe ich diese überprüft und es wird eine Abfrage für alle Benutzer und alle Daten. Es basiert auf Spencer ' s 1. (ein Witz?) Lösung, aber mir arbeitet.
Update: verbesserte Datums-handling in der zweiten Lösung.
InformationsquelleAutor der Antwort Stephen Perelson
Diese sollte das tun, was Sie wollen, aber ich habe nicht genug Daten zum testen der Effizienz. Die gewundenen KONVERTIEREN/FLOOR-Sachen ist, Streifen Sie die Zeit Teil aus dem datetime-Feld. Wenn Sie SQL Server 2008 verwenden, dann könnten Sie verwenden Sie CAST(x.ERSTELLUNGSDATUM (DATUM).
Erstellungsskript
InformationsquelleAutor der Antwort Dave Barker
Spencer fast Tat es, aber dies sollte den funktionierenden code:
InformationsquelleAutor der Antwort Recep
Aus der Spitze von meinem Kopf, MySQLish:
Ungetestet, und fast braucht sicherlich einige Umstellung für MSSQL, aber ich denke, dass einige Ideen.
InformationsquelleAutor der Antwort Cebjyre
Wie wäre es mit Tally-Tabellen? Es folgt eine weitere algorithmischen Ansatz, und der Ausführungsplan ist ein Kinderspiel. Füllen Sie die tallyTable mit zahlen von 1 bis 'MaxDaysBehind", die Sie Scannen möchten, mit der Tabelle (dh. 90 Aussehen wird für 3 Monate im Rückstand,etc).
InformationsquelleAutor der Antwort Radu094
Tweaking-Bill-Abfrage ein bisschen. Haben Sie vielleicht abschneiden, das Datum vor der Gruppierung zu zählen, nur eine Anmeldung pro Tag...
BEARBEITET zu verwenden, DATEADD(dd, DATEDIFF(dd, 0, CreationDate), 0) anstelle von convert( char(10) , CreationDate, 101 ).
@IDisposable
Ich war auf der Suche zu verwenden, datepart früher, aber ich war zu faul zum nachschlagen der syntax, so dass ich dachte, ich d verwenden, konvertieren statt. Ich kannte es hatte einen signifikanten Einfluss Dank! jetzt weiß ich,.
InformationsquelleAutor der Antwort Jaskirat
angenommen, ein schema, das sich wie:
dadurch extrahieren Sie zusammenhängende Bereiche, die aus einer Datum-Sequenz mit Lücken.
InformationsquelleAutor der Antwort Vincent Buck