2010-03-18 4 views
5

J'ai un schéma de base de données qui est similaire à ce qui suit:conseils de l'algorithme pour trouver des articles maximum dans une période de temps

| 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 

Je voudrais produire une requête qui renvoie le nombre de fois qu'un utilisateur effectue une action dans une période de temps. Par exemple, compte tenu d'une période de 5 jours, quel est le nombre maximum de fois que l'utilisateur 111 a marché avec le chien? La solution la plus évidente serait de commencer à un point zéro et avancer chaque jour, en additionnant des périodes de 5 jours en cours de route, puis en prenant le total maximum de toutes les fenêtres de 5 jours. l'approche semble cependant incroyablement coûteuse.

J'apprécierais toutes les suggestions que vous pourriez avoir.

EDIT 1:

Merci pour les commentaires/réponses. Pour répondre: - J'utilise mySQL v5.0 - Il pourrait y avoir un certain nombre d'événements par jour (pour toute période de temps vraiment) - @Paulo Santos: merci, mais comme le remarque le point, je dois trouver la fenêtre qui produit le plus de résultats, la fenêtre elle-même peut glisser. - @Mark: cela ressemble à une solution intéressante, bien que je me rappelle avoir lu que mySQL ne supporte pas les curseurs de sauvegarde ou de saut.
- @orbMan: cela semble prometteur. Je ne comprends pas encore tout à fait, mais je vais essayer ce soir. - @mjv: une autre solution prometteuse. semble également compliqué, mais je vais donner un autre coup d'oeil

merci encore!

+1

Utilisez-vous un SGBD qui prend en charge SQL ou est-il une source de fichier plat ou quelque chose comme ça, qui ne dispose pas d'un langage de requête? – AxelEckenberger

+0

Peut-il y avoir seulement zéro ou un événement par jour? –

Répondre

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 

Sortie:

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

Mise à jour avec une version plus permissive qui commence à compter les jours de n'importe quel jour d'événement. – RedFilter

3

Pour vous spécifique demande que je ferais quelque chose comme:

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

Ensuite, il retournera le nombre de fois chaque utilisateur effectué chaque tâche spécifié (@d1 et @d2) laps de temps.

+1

Je ne pense pas que ce soit exactement ce que Darren demande. Au contraire, il veut trouver la période de cinq jours dans laquelle l'utilisateur a exécuté la tâche le plus souvent. Il demande s'il existe un moyen de déterminer cela sans exécuter votre requête pour * chaque * ensemble de cinq jours contigus. – Callahad

+0

Je vais y jeter un coup d'œil ... Ce n'est pas trivial, mais ce n'est pas si difficile non plus. –

1

Voici un autre algorithme basé sur le curseur.

Démarrer avec deux curseurs, commencent et se terminent, les deux pointant sur la ligne initiale, et le nombre actuel = 0, et courant maximum = 0.

Si DATE_DIFF (end.date, begin.date) est plus 5, avancez le curseur de départ d'une ligne. Soustrayez un du nombre actuel si l'ancienne rangée était «promené le chien».

Si DATE_DIFF (end.date, begin.date) ne dépasse pas 5, faites avancer le curseur de fin d'une ligne. Aadd un compte courant si la nouvelle rangée est «promené le chien». Si le nombre actuel est supérieur au maximum actuel, définissez le maximum actuel sur le nombre actuel.

Continuez jusqu'à couvrir toutes les lignes de la plage.

1

Le code SQL suivant résout le problème d'une manière déclarative, plutôt que d'une manière purement procédurale/algorithmique. Selon la situation, il est probablement plus efficace (par rapport à l'obtention des données [triées] à partir de SQL, puis à l'exécution d'un algorithme, et même par rapport à des solutions basées sur le curseur côté serveur.

L'idée est d'obtenir le nombre d'événements [pertinents/filtrés], par utilisateur, par jour dans un tableau séparé ou CTE. puis pour chaque jour + utilisateur, pour compter le nombre d'événements pour ce jour et pour les 4 prochains jours, et enfin pour sélectionner (par utilisateur) la ligne avec le maximum de ces points.

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 
) 

Notes:
- Il utilise une table temporaire, même si une expression de table commune (CTE) pourrait être utilisé à la place en fonction de l'hôte SQL sous-jacente.
- Le nom/syntaxe particulier de la fonction DateAdd() peut varier entre les implémentations SQL.
- Cela implique également que le champ "date" contienne "seulement" une date, c'est-à-dire soit une date, soit un datetime/smalldatetime où la partie horaire est fixée (à savoir 00:00). Si tel n'était pas le cas, c'est-à-dire si la base de données contenait la date et l'heure dans la colonne, cela pourrait être corrigé au niveau de la requête CTE/table temporaire.

Questions connexes