Problème - Récupérez la somme des sous-statiques sur une demi-heure d'intervalle efficacement forte> J'utilise MySQL et j'ai une table contenant des sous-totaux à des moments différents. Je souhaite récupérer la somme de ces ventes sur un intervalle d'une demi-heure de 7h à 12h00. Ma solution actuelle (ci-dessous) fonctionne, mais il faut 13 secondes pour interroger environ 150 000 enregistrements. J'ai l'intention d'avoir plusieurs millions d'enregistrements à l'avenir et ma méthode actuelle est trop lente. P> Comment puis-je rendre cela plus efficace ou si possible, remplacez le composant PHP avec pure SQL? En outre, cela aiderait-il votre solution à être encore plus efficace si j'ai utilisé des horodatages UNIX au lieu d'avoir une colonne de date et d'heure? P> for($n = strtotime("07:00:00"), $e = strtotime("23:59:59"); $n <= $e; $n += 1800) {
$timeA = date("H:i:s", $n);
$timeB = date("H:i:s", $n+1799);
$query = $mySQL-> query ("SELECT SUM(subtotal)
FROM Receipts WHERE time > '$timeA'
AND time < '$timeB'");
while ($row = $query-> fetch_object()) {
$sum[] = $row;
}
}
7 Réponses :
Premièrement, j'utiliserais une seule colonne DateTime, mais utiliser une colonne de date et heure fonctionnera.
Vous pouvez effectuer tout le travail en une seule passe à l'aide d'une seule requête: P>
select date,
hour(`time`) hour_num,
IF(MINUTE(`time`) < 30, 0, 1) interval_num,
min(`time`) interval_begin,
max(`time`) interval_end,
sum(subtotal) sum_subtotal
from receipts
where date='2012-07-31'
group by date, hour_num, interval_num;
Je reçois un résultat bizarre. Il génère la somme du sous-total sur un intervalle quotidien. Collé le rendement de la matrice ici: Pastebin.com/16QPZJ5T
Avez-vous utilisé accidentellement le «temps» (citations simples) au lieu de temps code> (backticks)? Il ressemble à min (heure), max (heure) renvoie des valeurs littérales "temps".
@greenlion Oops j'ai utilisé du temps avec des citations au lieu de temps. Voici le vidage de la matrice mise à jour. Je fais probablement quelque chose de mal, mais il semble que le résultat sort comme le sous-total par jour pour la plage de temps donnée. Pastebin.com/pm0mprmr
Veuillez poster le SQL exact que vous avez couru avec le résultat. Merci.
Vous pouvez également essayer cette requête unique, il doit renvoyer un ensemble de résultats avec les totaux en groupe de 30 minutes: Pour exécuter cela efficacement, ajoutez un indice composite à la date et Colonnes de temps. P> Vous devez récupérer un résultat défini comme: p>
Votre requête suppose que la valeur code> code> que MySQL choisit sera la période min, ce qui suppose une commande de table naturelle triée dans la commande de temps croissant, ce qui peut ne pas être vrai. Vous devez utiliser min ( heure code>) dans la clause Select.
@greenlion merci, je l'ai édité d'utiliser min code> sur la colonne TEMPS.
@greenlion Bon appel sur la minute Les résultats renvoyés sont correctement intervalles.
@ Drew010 Merci pour cela, cette solution est ma préférée jusqu'à présent.
@PonTustrade Merci, je l'ai testé sur une table de mine qui avait une colonne code> code> tel que reflété dans ma sortie. De toute évidence, vos résultats dans la colonne code> code> seront simplement les moments. Je viens de modifier la requête pour sélectionner aussi la date juste FYI.
Un moyen de le faire pure SQL est d'utiliser une table de recherche. Je ne connais pas mysql que oui peut-être peut-être beaucoup d'amélioration du code. Tout mon code sera MS SQL .. Je le ferais comme ceci:
/* Mock salesTable */
Declare @SalesTable TABLE (SubTotal int, SaleDate datetime)
Insert into @SalesTable (SubTotal, SaleDate) VALUES (1, '2012-08-01 12:00')
Insert into @SalesTable (SubTotal, SaleDate) VALUES (2, '2012-08-01 12:10')
Insert into @SalesTable (SubTotal, SaleDate) VALUES (3, '2012-08-01 12:15')
Insert into @SalesTable (SubTotal, SaleDate) VALUES (4, '2012-08-01 12:30')
Insert into @SalesTable (SubTotal, SaleDate) VALUES (5, '2012-08-01 12:35')
Insert into @SalesTable (SubTotal, SaleDate) VALUES (6, '2012-08-01 13:00')
Insert into @SalesTable (SubTotal, SaleDate) VALUES (7, '2012-08-01 14:00')
/* input data */
declare @From datetime, @To DateTime, @intervall int
set @from = '2012-08-01'
set @to = '2012-08-02'
set @intervall = 30
/* Create lookup table */
DECLARE @lookup TABLE (StartTime datetime, EndTime datetime)
DECLARE @tmpTime datetime
SET @tmpTime = @from
WHILE (@tmpTime <= @To)
BEGIN
INSERT INTO @lookup (StartTime, EndTime) VALUES (@tmpTime, dateAdd(mi, @intervall, @tmpTime))
set @tmpTime = dateAdd(mi, @intervall, @tmpTime)
END
/* Get data */
select l.StartTime, l.EndTime, sum(subTotal) from @SalesTable as SalesTable
join @lookUp as l on SalesTable.SaleDate >= l.StartTime and SalesTable.SaleDate < l.EndTime
group by l.StartTime, l.EndTime
Dans ma requête, je suppose un domaine DateTime nommé date. Cela vous donnera tous les groupes à partir de toute date de date à partir de laquelle vous le donnez pour commencer:
SELECT ABS(FLOOR(TIMESTAMPDIFF(MINUTE, date, '2011-08-01 00:00:00') / 30)) AS GROUPING , SUM(subtotal) AS subtotals FROM Receipts GROUP BY ABS(FLOOR(TIMESTAMPDIFF(MINUTE, date, '2011-08-01 00:00:00') / 30)) ORDER BY GROUPING
Utilisez toujours les types de données appropriés pour vos données. Dans le cas de vos colonnes de date / heure, il est préférable de les stocker comme (de préférence UTC zoné). Cela est particulièrement vrai en ce que certains moments n'existent pas pour certaines dates (pour certains horloges, d'où l'UTC). Vous voudrez un index sur cette colonne.
En outre, votre plage de date / heure ne vous donnera pas ce que vous voulez - à savoir, vous ne vous manquez pas exactement l'heure (parce que vous utilisez un strict supérieur à Comparaison). Définissez toujours des gammes en tant que «EXCLUSIVE INCLUS INCLUSIVE INCLUS INCLUS INCLUSIMENT INCLUSIVÉE» (SO, Parce que MySQL n'a pas de requêtes récursives, vous voulez que quelques tables supplémentaires tirent celles-ci. Je les référencez de tables «permanentes», mais il serait certainement possible de les définir en ligne, si nécessaire. P> Vous allez vouloir une table de calendrier. Celles-ci sont utiles pour un certain nombre de raisons, mais ici, nous les voulons pour leur liste de dates. Cela nous permettra de montrer des dates qui ont des sous-totales de 0, si nécessaire. Vous allez également vouloir avoir une valeur de fois des incréments de demi-heure, pour les mêmes raisons. P> Ceci devrait vous permettre d'interroger vos données comme: p> ( Exemple de fonctionnement sur SQLFIDDLE , qui utilise un régulier TIME> = '07: 00: 00 'et heure <'07: 30: 00' code>). Ceci est particulièrement important pour les horodatages, qui ont un nombre supplémentaire de champs à traiter. P> rejoindre code> pour brièveté) p> p>
MISE À JOUR: B>
Comme vous n'êtes pas préoccupé par des lignes «manquantes», je vais également assumer (probablement à tort) que vous n'êtes pas préoccupé par le fait que la requête pourrait éventuellement Retournez des rangées pour des périodes qui ne sont pas de 7h à 12h. Cette requête retournera votre ensemble de résultats spécifié: p>
SELECT MAKETIME(23,59,59) < MAKETIME(24,0,0)
Salut Spencer. Votre solution renvoie les résultats exacts dont j'ai besoin et de la même manière que ma boucle actuelle. Mon seul problème est qu'il a toujours pris 9 secondes pour exécuter l'ensemble du script, tandis que la solution proposée par Drew à Stackoverflow.com/a/11768140 / 1547373 a pris une demi-seconde. Peut-être que je manque quelque chose pourquoi ça prend ça long? Aussi bon point sur les lignes manquantes quand il n'existe pas de temps. Il y a suffisamment de données afin que ce ne soit pas un problème cependant. Je veux récompenser les deux réponses comme correctes car elles sont toutes deux, juste de différentes manières.
Salut pontus. 9 secondes de temps écoulées sont beaucoup plus longues que ce que j'aurais attendu. La solution de Drew implique une date, mais la solution que je n'ai pas. La performance devrait certainement être plus rapide, mais j'avais vraiment besoin d'examiner l'explication et les définitions de la table, y compris les indices. La requête que j'ai fournie va traiter chaque de ces 150 000 lignes et les assignera tous dans 33 "godets". En raison de l'opération de jointure dans cette requête, cela bénéficiera d'index appropriés.
@Pontus: J'ai mis à jour ma réponse pour ajouter une requête qui devrait renvoyer le résultat spécifié plus efficacement que ma réponse précédente. Cela renvoie les deux colonnes: i (comme identifiant d'époque comme indiqué dans la question et un sous-total.
J'ai aussi trouvé une solution différente et l'affiche ici pour référence si quelqu'un devra-t-il trébucher. Groupes de demi-heure.
SELECT SUM(total), time, date FROM tableName GROUP BY (2*HOUR(time) + FLOOR(MINUTE(time)/30))
@Radashk the Sale_ID est l'index principal et des liens vers une autre table appelée vente aux produits vendus par réception. Certains recettes ont 3 produits vendus, tandis que d'autres n'en ont qu'un pour que je l'ai séparé dans une base de données relationnelle.
Indexez la colonne de temps. Je ne vois aucune amélioration considérable d'autre
J'ai une réponse ici: Stackoverflow.com/a/11367541/9094 qui vous permet d'interroger avec des groupes en fonction du temps arbitraire Intervalles, vous pouvez probablement l'adopter pour répondre à vos besoins.
@Radashk J'ai indexé l'identifiant de l'heure comme vous suggéré et que la vitesse de requête est maintenant à 1,5 seconde. Awesome Stuff Buddy. Bien que votre solution soit grande, et je ne veux pas réduire votre aide et votre expertise, j'espère qu'une solution pouvant être faite à l'aide de SQL seule.
Si j'ai le temps plus tard ce soir, je vais assembler une solution potentielle détaillée, mais brièvement: Ne séparez pas vos champs de date et d'heure, utilisez un seul champ. L'horodatage unique ou la date d'heure n'a pas d'importance. Cela facilite les choses de la porte. Vous devriez obtenir toutes vos données dans une seule requête, laissez MySQL le séparer en groupes au lieu d'utiliser PHP. Utilisez les fonctions de date / heure pour convertir votre champ DateTime en heures et minutes, puis utilisez
groupe par code> sur ces champs pour regrouper les heures / demi-heures ensemble et générer les sommes.