2011-06-28 7 views
2


Je crée un graphique Web basé sur jquery qui affiche les données météorologiques. Les données sont chargées via Ajax -> PHP -> MySQL. La base de données MySQL contient un enregistrement de divers paramètres pour chaque minute pour les 10 dernières années ... (Donc une quantité énorme d'enregistrements). Je souhaite que les utilisateurs puissent générer un graphique pour une plage de dates personnalisée, mais limiter le nombre de données que je tire pour des plages de dates plus longues que quelques jours. Autrement dit, s'ils interrogent pendant une semaine de données, mon code php devrait renvoyer seulement 1 valeur de données par heure.
Je ne veux pas effectuer de calcul de moyenne ou de traitement côté serveur de ce type - je veux récupérer seulement chaque énième enregistrement de mysql - de sorte que j'obtienne une quantité gérable de données.date nième enregistrement dans la requête MySQL

Mon problème est, ma base de données n'a pas de champ de numéro d'enregistrement, et je ne peux pas modifier le format de la base de données. Y a-t-il un moyen de le faire en fonction des valeurs de date réelles? Comme dit cast à l'horodatage unix, alors sélectionnez seulement l'enregistrement si la date est divisible par un certain nombre? (Je calcule le nombre basé sur la longueur de la plage de temps, pour tirer un nombre fixe de points)

Des idées sur les bonnes façons de le faire? S'il y avait une solution qui me permettrait de sélectionner directement les intervalles de temps, ce serait idéal. (c'est-à-dire toutes les 5 minutes, 10 minutes, 1 heure, 5 heures, etc.)

EDIT: le champ est le format MySQL dateTime !! Merci d'avoir demandé la clarification!

+0

Dans quel format est l'heure stockée dans la base de données (la colonne est-elle une colonne datetime mysql ou timestamp unix)? Et cette colonne est-elle indexée? – Arjan

+0

Pouvez-vous ajouter les détails de votre table? –

+0

Désolé! Merci pour les commentaires. C'est une colonne datetime MySQL! –

Répondre

1

Vous pouvez utiliser les MOD() et UNIX_TIMESTAMP() fonctions dans votre clause SQL WHERE

SELECT * FROM WEATHER WHERE MOD(UNIX_TIMESTAMP(Time), Divisor) = 0

Est-ce que vous obtenez seulement des enregistrements avec des temps qui sont divisibles par Diviseur. Le Diviseur serait l'incrément de temps pour lequel vous souhaitez récupérer des données (300 pour toutes les 5 minutes, 5400 pour toutes les 1,5 heures, etc.).

Étant donné que UNIX Time utilise un int de 32 bits, votre type de données INT standard dans MySQL ira très bien.

+1

Si la requête est utilisée pour de grands intervalles (ex .: 5 ans) alors cette requête ne sera pas assez rapide. En tout cas je pense qu'il est difficile de trouver une bonne solution sans modification de la base de données. Le problème est que tout calcul sur le champ datetime signifie que l'index de ce champ ne sera pas utilisé dans la requête. – Karolis

+0

@Karolis Mise à jour de la réponse. – Dirk

+0

Ça a l'air bien. C'est à peu près ce que je cherchais. Je vais essayer demain et faire un rapport. Merci! :) –

0

Vous pouvez convertir un DATETIME en un horodatage UNIX, diviser par le nombre de secondes dans l'intervalle souhaité (10 minutes dans l'exemple suivant), puis utiliser GROUP BY pour réduire à une ligne par valeur distincte.

SELECT FLOOR(UNIX_TIMESTAMP(datetime_col)/600) AS ts, * FROM WEATHER 
WHERE datetime_col BETWEEN ? AND ? 
GROUP BY ts 

Cela ne va pas être très rapide, car il doit calculer ts pour chaque ligne, puis groupe en ce qu'une colonne non indexée.

Cela dépend également du comportement non standard de MySQL, qui autorise des requêtes GROUP BY ambiguës. C'est-à-dire qu'il renvoie une ligne arbitraire du groupe, déterminée par le moteur de stockage. En pratique, il s'agit de la première ligne stockée physiquement, mais cela peut prêter à confusion en fonction du moteur de stockage, des index de couverture, etc.

Alternative: Vous pouvez utiliser une variable utilisateur pour compter les lignes et ne renvoyer que la première changements d'intervalle.

SET @interval := 0; 
SET @row := 0; 
SELECT t.* FROM (
SELECT (@i:=FLOOR(UNIX_TIMESTAMP(datetime_col)/600)), 
    IF(@interval<>@i),@row:=0,@row:[email protected]+1) AS row, @interval:[email protected], * 
FROM WEATHER 
WHERE datetime_col BETWEEN ? AND ? 
) AS t 
WHERE t.row = 0; 
0

Comme dire jeté à timestamp unix, alors seulement sélectionner l'enregistrement si la date est divisible par un nombre?

Le problème avec cela, et la plupart des approches est que vous avez encore à lire tous les points consécutifs (sauf si vous avez des colonnes indexées contenant différentes représentations de l'horodatage) de sorte que vous pourriez réduire la taille de l'ensemble de résultats mais pas la quantité de travail nécessaire pour l'extraire.

Pouvez-vous créer une nouvelle table (ne doit pas être dans la même base de données/serveur - vous pouvez toujours vous connecter aux données brutes en utilisant le moteur fédéré)? De cette façon, vous pouvez mettre en place une table d'horodatages avec différents degrés de granularité, dans une série de tours de Hanoi, par ex.

date time  level 
------------- ----- 
201101010000 0 
201101010010 6 
201101010020 6 
201101010030 5 
201101010040 6 
201101010050 6 
201101010100 4 
201101010110 6 
201101010120 6 
201101010130 5 
... 
201101020000 3 
... 

De cette façon, vous pourriez choisir cette histoire à un niveau approprié de granularité et rejoindre les données brutes sous-jacentes. Ce qui précède peut être incorporé en tant que fonction pour agréger des données - mais sans pouvoir rechercher depuis, vous devez toujours lire toutes les lignes intermédiaires dans les données src.

S'il y avait une solution qui me permettrait de sélectionner directement même des intervalles de temps

Quelque chose comme ....

SELECT DATE_FORMAT(yourdate, SUBSTR('%Y%m%d%H%i%s',0,2*@level)) as t, 
AVG(value) 
FROM yourtable 
WHERE yourdate BETWEEN @datestart AND @dateend 
GROUP BY DATE_FORMAT(yourdate, SUBSTR('%Y%m%d%H%i%s',0,2*@level)) 
ORDER BY 1; 

(comme ci-dessus - sans une seconde table pour rejoindre ou d'une autre manière de sélectionner uniquement l'échantillon de données désiré en utilisant un index, il n'y a pas de pénalité de performance en utilisant l'agrégat fn).

Questions connexes