Le tableau suivant de l'historique des utilisateurs contient un enregistrement pour chaque jour où un utilisateur donné accède à un site Web (dans une période de 24 heures UTC). Il contient plusieurs milliers d'enregistrements, mais un seul enregistrement par jour et par utilisateur. Si l'utilisateur n'a pas accédé au site Web ce jour-là, aucun enregistrement ne sera généré.
ID UserId CreationDate ------ ------ ------------ 750997 12 07/07/2009 18: 42: 20.723 750998 15 07/07/2009 18: 42: 20.927 751000 19 07/07/2009 18: 42: 22.283
Ce que je recherche, c'est une requête SQL sur cette table avec de bonnes performances , qui me dit quels userids ont accédé au site pendant (n) jours consécutifs sans manquer un jour.
En d'autres termes, combien d'utilisateurs ont (n) enregistrements dans cette table avec des dates séquentielles (jour avant ou après) ? Si un jour est absent de la séquence, la séquence est interrompue et doit recommencer à 1; nous recherchons des utilisateurs qui ont accompli un nombre continu de jours ici sans interruption.
Toute ressemblance entre cette requête et un badge Stack Overflow particulier est purement fortuite, bien sûr .. :)
la source
Réponses:
La réponse est évidemment:
ÉDITER:
Ok, voici ma réponse sérieuse:
ÉDITER:
[Jeff Atwood] C'est une excellente solution rapide et mérite d'être acceptée, mais la solution de Rob Farley est également excellente et sans doute encore plus rapide (!). S'il vous plaît vérifier aussi!
la source
ON uh2.CreationDate >= uh1.CreationDate AND uh2.CreationDate < DATEADD(dd, DATEDIFF(dd, 0, uh1.CreationDate) + @days, 0)
en:, pour signifier "Pas encore le 31ème jour plus tard". Cela signifie également que vous pouvez ignorer le calcul des @seconds.Que diriez-vous (et assurez-vous que la déclaration précédente se termine par un point-virgule):
L'idée étant que si nous avons la liste des jours (sous forme de nombre), et un row_number, alors les jours manqués augmentent légèrement le décalage entre ces deux listes. Nous recherchons donc une fourchette qui présente un décalage constant.
Vous pouvez utiliser "ORDER BY NumConsecutiveDays DESC" à la fin de ceci, ou dire "HAVING count (*)> 14" pour un seuil ...
Je n'ai pas testé cela cependant - je l'ai simplement écrit par le haut de ma tête. Espérons que cela fonctionne dans SQL2005 et autres.
... et serait très aidé par un index sur le nom de la table (UserID, CreationDate)
Modifié: Il s'avère que Offset est un mot réservé, j'ai donc utilisé TheOffset à la place.
Modifié: La suggestion d'utiliser COUNT (*) est très valable - j'aurais dû le faire en premier lieu mais je n'y pensais pas vraiment. Auparavant, il utilisait à la place datéiff (jour, min (CreationDate), max (CreationDate)).
Rob
la source
Si vous pouvez modifier le schéma de la table, je vous suggère d'ajouter une colonne
LongestStreak
à la table que vous définiriez sur le nombre de jours séquentiels se terminant parCreationDate
. Il est facile de mettre à jour le tableau au moment de la connexion (comme vous le faites déjà, si aucune ligne n'existe pour le jour en cours, vous vérifierez si une ligne existe pour le jour précédent. Si la valeur est true, vous incrémenterez leLongestStreak
dans le nouvelle ligne, sinon, vous la définissez sur 1.)La requête sera évidente après l'ajout de cette colonne:
la source
Un SQL joliment expressif comme:
En supposant que vous ayez une fonction d'agrégation définie par l'utilisateur, quelque chose du genre (attention, c'est bogué):
la source
On dirait que vous pourriez tirer parti du fait que pour être continu sur n jours, il faudrait qu'il y ait n lignes.
Donc quelque chose comme:
la source
Faire cela avec une seule requête SQL me semble trop compliqué. Permettez-moi de diviser cette réponse en deux parties.
Exécutez une tâche cron quotidienne qui vérifie pour chaque utilisateur s'il s'est connecté aujourd'hui, puis incrémente un compteur s'il l'a ou le met à 0 s'il ne l'a pas fait.
- Exportez cette table vers un serveur qui n'exécute pas votre site Web et ne sera pas nécessaire pendant un certain temps. ;)
- Triez-le par utilisateur, puis par date.
- parcourez-le séquentiellement, gardez un compteur ...
la source
Si cela est si important pour vous, recherchez cet événement et gérez une table pour vous donner cette information. Pas besoin de tuer la machine avec toutes ces requêtes folles.
la source
Vous pouvez utiliser un CTE récursif (SQL Server 2005+):
la source
Joe Celko a un chapitre complet à ce sujet dans SQL for Smarties (en l'appelant Runs and Sequences). Je n'ai pas ce livre à la maison, alors quand j'arriverai au travail ... je répondrai à ça. (en supposant que la table d'historique s'appelle dbo.UserHistory et que le nombre de jours est @Days)
Une autre piste provient du blog de SQL Team sur les exécutions
L'autre idée que j'ai eue, mais je n'ai pas de serveur SQL sur lequel travailler ici, est d'utiliser un CTE avec un ROW_NUMBER partitionné comme ceci:
Ce qui précède est probablement BEAUCOUP PLUS DIFFICILE qu'il ne doit l'être, mais laissé comme un chatouillement cérébral lorsque vous avez une autre définition de "une course" que de simples dates.
la source
Quelques options SQL Server 2012 (en supposant N = 100 ci-dessous).
Bien qu'avec mes exemples de données, les éléments suivants ont été plus efficaces
Tous deux reposent sur la contrainte énoncée dans la question selon laquelle il y a au plus un enregistrement par jour et par utilisateur.
la source
Quelque chose comme ça?
la source
J'ai utilisé une propriété mathématique simple pour identifier qui a accédé consécutivement au site. Cette propriété est que vous devez avoir la différence de jour entre le premier accès et la dernière fois égale au nombre d'enregistrements dans votre journal de table d'accès.
Voici le script SQL que j'ai testé dans Oracle DB (il devrait également fonctionner dans d'autres DB):
Script de préparation de table:
la source
L'instruction
cast(convert(char(11), @startdate, 113) as datetime)
supprime la partie horaire de la date, nous commençons donc à minuit.Je suppose également que les colonnes
creationdate
etuserid
sont indexées.Je viens de réaliser que cela ne vous dira pas tous les utilisateurs et leur nombre total de jours consécutifs. Mais vous dira quels utilisateurs auront visité un certain nombre de jours à partir d'une date de votre choix.
Solution révisée:
J'ai vérifié cela et il interrogera tous les utilisateurs et toutes les dates. Il est basé sur la première solution (blague?) De Spencer , mais la mienne fonctionne.
Mise à jour: amélioration de la gestion des dates dans la deuxième solution.
la source
Cela devrait faire ce que vous voulez, mais je n'ai pas assez de données pour tester l'efficacité. Le truc convoluté CONVERT / FLOOR consiste à supprimer la partie heure du champ datetime. Si vous utilisez SQL Server 2008, vous pouvez utiliser CAST (x.CreationDate AS DATE).
Script de création
la source
Spencer l'a presque fait, mais cela devrait être le code de travail:
la source
Du haut de ma tête, MySQLish:
Non testé, et nécessite presque certainement une conversion pour MSSQL, mais je pense que cela donne des idées.
la source
Que diriez-vous d'utiliser des tableaux de pointage? Il suit une approche plus algorithmique et le plan d'exécution est un jeu d'enfant. Remplissez le tallyTable avec les nombres de 1 à «MaxDaysBehind» que vous voulez analyser le tableau (c'est-à-dire que 90 recherchera 3 mois de retard, etc.).
la source
Ajuster un peu la requête de Bill. Vous devrez peut-être tronquer la date avant le regroupement pour ne compter qu'une seule connexion par jour ...
EDITED pour utiliser DATEADD (dd, DATEDIFF (dd, 0, CreationDate), 0) au lieu de convert (char (10), CreationDate, 101).
@IDisposable Je cherchais à utiliser datepart plus tôt, mais j'étais trop paresseux pour rechercher la syntaxe, alors j'ai pensé que j'utiliserais plutôt convert. Je ne sais pas que cela a eu un impact significatif Merci! maintenant je sais.
la source
en supposant un schéma qui va comme:
cela extraira des plages contiguës d'une séquence de dates avec des intervalles.
la source