2009-08-15 6 views
2

J'ai une table avec des enregistrements qui ressemblent à ceci:Requête SQL pour le nombre d'enregistrements correspondant au jour d'une plage de dates?

CREATE TABLE sample (
    ix int unsigned auto_increment primary key, 
    start_active datetime, 
    last_active datetime 
); 

Je dois savoir combien de dossiers ont été actifs sur chacun des 30 derniers jours. Les jours doivent également être triés de façon à ce qu'ils soient retournés du plus ancien au plus récent. J'utilise MySQL et la requête sera exécutée à partir de PHP mais je n'ai pas vraiment besoin du code PHP, juste de la requête.

Voici mon départ:

SELECT COUNT(1) cnt, DATE(?each of last 30 days?) adate 
FROM sample 
WHERE adate BETWEEN start_active AND last_active 
GROUP BY adate; 

Répondre

5

Effectuez une jointure externe.

Pas de tableau? Faire une table. Je garde toujours une table fictive juste pour ça.

create table artificial_range( 
    id int not null primary key auto_increment, 
    name varchar(20) null) ; 

-- or whatever your database requires for an auto increment column 

insert into artificial_range(name) values (null) 
-- create one row. 

insert into artificial_range(name) select name from artificial_range; 
-- you now have two rows 

insert into artificial_range(name) select name from artificial_range; 
-- you now have four rows 

insert into artificial_range(name) select name from artificial_range; 
-- you now have eight rows 

--etc. 

insert into artificial_range(name) select name from artificial_range; 
-- you now have 1024 rows, with ids 1-1024 

Maintenant, appropriez pratique à utiliser, et limiter à 30 jours, en vue:

Edit: JR Lawhorne Notes:

Vous devez changer "date_add" à " date_sub "pour obtenir les 30 derniers jours dans la vue créée.

Merci JR!

create view each_of_the_last_30_days as 
select date_sub(now(), interval (id - 1) day) as adate 
from artificial_range where id < 32; 

utiliser maintenant dans votre requête (je ne l'ai pas testé en fait votre requête, je suis juste en supposant qu'il fonctionne correctement):

Edit: je devrais rejoindre l'autre sens:

SELECT COUNT(*) cnt, b.adate 
FROM each_of_the_last_30_days b 
left outer join sample a 
on (b.adate BETWEEN a.start_active AND a.last_active) 
GROUP BY b.adate; 
+0

Vous devez remplacer "date_add" par "date_sub" pour obtenir les 30 jours précédents dans la vue créée. –

0

SQL est grande à faire correspondre des ensembles de valeurs qui sont stockées dans la base de données, mais il est pas si grand à Assemblez des valeurs qui ne sont pas dans le base de données. Donc, une solution de contournement simple est de créer une table temporaire contenant les valeurs dont vous avez besoin:

CREATE TEMPORARY TABLE days_ago (d SMALLINT); 
INSERT INTO days_ago (d) VALUES 
    (0), (1), (2), ... (29), (30); 

Maintenant, vous pouvez comparer une date il y a d jours à la durée entre start_active et last_active de chaque ligne. Comptez combien de lignes correspondantes dans le groupe par valeur de d et vous avez votre compte.

SELECT CURRENT_DATE - d DAYS, COUNT(*) cnt, 
FROM days_ago 
LEFT JOIN sample ON (CURRENT_DATE - d DAYS BETWEEN start_active AND last_active) 
GROUP BY d 
ORDER BY d DESC; -- oldest to newest 

Une autre note: vous ne pouvez pas utiliser des alias de colonne définis dans votre liste de sélection dans les expressions jusqu'à ce que vous obtenez à la clause GROUP BY. En fait, en SQL standard, vous ne pouvez pas les utiliser jusqu'à la clause ORDER BY, mais MySQL prend en charge l'utilisation d'alias dans les clauses GROUP BY et HAVING.

0

Tourner la date dans un horodatage unix, qui est seconde, dans votre requête, puis il suffit de regarder pour que la différence soit < = le nombre de secondes dans un mois.

Vous trouverez plus d'informations ici: http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_unix-timestamp

Si vous avez besoin d'aide avec la requête s'il vous plaît laissez-moi savoir, mais MySQL a de belles fonctions pour traiter datetime.

[Modifier] Comme j'étais confus quant à la vraie question, je dois finir la pelouse mais avant que j'oublie je veux écrire cela. Pour obtenir le nombre de fois par jour, vous voulez que votre clause where soit comme je l'ai décrit ci-dessus, pour la limiter aux 30 derniers jours, mais vous devrez vous grouper par jour, et ainsi sélectionner en convertissant chaque début à un jour du mois et ensuite faire un compte de ceux-ci.

Cela suppose que chaque utilisation sera limitée à un jour, si les dates de début et de fin peuvent s'étendre sur plusieurs jours, il sera plus difficile.

+0

Transférer quelle date dans un horodatage unix? 'CURRENT_DATE - INTERVAL 30 DAY',' start_active' ou 'end_active'? Comment cela aide-t-il le groupe OP au nombre d'enregistrements actifs * pour chaque jour * des trente derniers? – pilcrow

+0

Transformez start_active et last_active en unix_timestamp, ou, bien que je ne l'aie pas essayé, vous pouvez utiliser subtime() pour soustraire les heures puis convertir, ou, dans la page suggérée ci-dessus, vous pouvez utiliser quelque chose comme ceci: à partir de MESSAGE où SENT_TIME <(CURDATE() - INTERVALLE 5 JOURS); Peu importe, il suffit de regarder à travers les fonctions de temps et vous devriez être en mesure de trouver ce qui fonctionnera le mieux pour vous. –

+0

@ James Black, votre réponse suggère que vous pensez que l'OP veut connaître le nombre d'enregistrements se chevauchant avec une fenêtre temporelle de 30 jours - mais c'est la mauvaise question, il veut plutôt connaître le nombre d'enregistrements qui se chevauchent * chaque jour * pour les 30 derniers jours. (Oui, l'arithmétique de la date est facile dans les types natifs SQL ou en utilisant des timbres d'époque entiers.) Peut-être pourriez-vous poster une solution de code pour illustrer votre proposition? – pilcrow

Questions connexes