Comment puis-je améliorer mon instruction SQL avec des résultats hebdomadaires avec une semaine commençant le jeudi ou tout autre jour de la semaine?


8

Je suis un newb total et je n'ai pas pu trouver un bon moyen de le faire n'importe où. J'ai une table de base de données qui contient des statistiques enregistrées à différents moments de la semaine. La semaine du rapport commence jeudi. Le tableau contient une colonne d'horodatage (date) qui stocke le moment où les données ont été enregistrées.

Je dois extraire les données pour une semaine donnée (les semaines de rapport commencent le jeudi). J'ai écrit la requête suivante:

   SELECT * 
FROM `table` 
WHERE 1 = CASE 
  WHEN WEEKDAY(NOW()) = 0 THEN DATEDIFF(NOW(),`date`) BETWEEN -2 AND 4
  WHEN WEEKDAY(NOW()) = 1 THEN DATEDIFF(NOW(),`date`) BETWEEN -1 AND 5
  WHEN WEEKDAY(NOW()) = 2 THEN DATEDIFF(NOW(),`date`) BETWEEN -0 AND 6
  WHEN WEEKDAY(NOW()) = 3 THEN DATEDIFF(NOW(),`date`) BETWEEN -6 AND 0
  WHEN WEEKDAY(NOW()) = 4 THEN DATEDIFF(NOW(),`date`) BETWEEN -5 AND 1
  WHEN WEEKDAY(NOW()) = 5 THEN DATEDIFF(NOW(),`date`) BETWEEN -4 AND 2
  WHEN WEEKDAY(NOW()) = 6 THEN DATEDIFF(NOW(),`date`) BETWEEN -3 AND 3
END

Cela semble fonctionner sur les tests initiaux. Mais je ne suis pas sûr que ce soit la meilleure façon de procéder. Je ne sais pas grand chose sur les performances de MySQL, mais il y aura plus de cent mille enregistrements à filtrer. Cette requête sera-t-elle vraiment lente en raison du nombre de conditions vérifiées?

La fonction NOW () est utilisée lors de l'extraction du rapport le plus récent - cependant, dans certains cas, je devrai faire des rapports pour d'autres semaines - donc je remplacerais une autre date à la place.

En outre, pour ce faire, vous devez réécrire la requête si la semaine de rapport change - par exemple, le jour de début passe à mercredi.

Je ne peux pas utiliser la fonction WEEK () car vous ne pouvez commencer qu'une semaine le dimanche ou le lundi avec.

Toutes les idées pour améliorer cette requête sont très appréciées!

Autres notes: utilise actuellement MariaDB 5.3.

Réponses:


5

Voici une requête que j'ai rédigée pour vous donner le jeudi le plus récent et le mercredi se terminant

SELECT thuwk_beg + INTERVAL 0 second thu_beg,
thuwk_beg + INTERVAL 604799 second wed_end
FROM (SELECT (DATE(NOW()) - INTERVAL daysbacktothursday DAY) thuwk_beg
FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE(NOW()) dt) AAAA) AAA) AA) A;

Voici un exemple pour aujourd'hui, 2011-09-21

mysql> SELECT
    -> thuwk_beg + INTERVAL 0 second thu_beg,
    -> thuwk_beg + INTERVAL 604799 second wed_end
    -> FROM (SELECT (DATE(NOW()) - INTERVAL daysbacktothursday DAY) thuwk_beg
    -> FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
    -> FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE(NOW()) dt) AAAA) AAA) AA) A;
+---------------------+---------------------+
| thu_beg             | wed_end             |
+---------------------+---------------------+
| 2011-09-15 00:00:00 | 2011-09-21 23:59:59 |
+---------------------+---------------------+
1 row in set (0.00 sec)

Remplacez simplement les appels de fonction NOW () par la date et l'heure que vous aimez et vous aurez la semaine commençant jeudi tout le temps pour la date et l'heure que vous choisissez.

Voici un autre exemple utilisant la date spécifique '2011-01-01'

mysql> SELECT
    -> thuwk_beg + INTERVAL 0 second thu_beg,
    -> thuwk_beg + INTERVAL 604799 second wed_end
    -> FROM (SELECT (DATE('2011-01-01') - INTERVAL daysbacktothursday DAY) thuwk_beg
    -> FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
    -> FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE('2011-01-01') dt) AAAA) AAA) AA) A;
+---------------------+---------------------+
| thu_beg             | wed_end             |
+---------------------+---------------------+
| 2010-12-30 00:00:00 | 2011-01-05 23:59:59 |
+---------------------+---------------------+
1 row in set (0.00 sec)

Votre requête de tableréférencement aujourd'hui ressemblerait à quelque chose comme ceci:

SELECT * from `table`,
(SELECT thuwk_beg + INTERVAL 0 second thu_beg,
thuwk_beg + INTERVAL 604799 second wed_end
FROM (SELECT (DATE(NOW()) - INTERVAL daysbacktothursday DAY) thuwk_beg
FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE(NOW()) dt) AAAA) AAA) AA) A) M
WHERE `date` >= thu_beg
AND `date` <= wed_end;

Essaie !!!

MISE À JOUR 2011-09-22 16:27 EDT

C'était ma requête proposée pour marquer Thu-Wed.

SELECT thuwk_beg + INTERVAL 0 second thu_beg,
thuwk_beg + INTERVAL 604799 second wed_end
FROM (SELECT (DATE(NOW()) - INTERVAL daysbacktothursday DAY) thuwk_beg
FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE(NOW()) dt) AAAA) AAA) AA) A;

Et les autres semaines ???

  • (SELECT SUBSTR('6012345',wkndx,1) la semaine commençant le lundi se terminant le soleil
  • (SELECT SUBSTR('5601234',wkndx,1) la semaine commençant le mardi se terminant le lundi
  • (SELECT SUBSTR('4560123',wkndx,1) la semaine commençant le mercredi se terminant le mardi
  • (SELECT SUBSTR('3456012',wkndx,1) la semaine commençant le jeudi se terminant le mercredi
  • (SELECT SUBSTR('2345601',wkndx,1) la semaine commençant le ven se terminant le jeu
  • (SELECT SUBSTR('1234560',wkndx,1) la semaine commençant le samedi se terminant le vendredi
  • (SELECT SUBSTR('0123456',wkndx,1) la semaine commençant le dimanche se terminant le samedi

1

Permettez-moi de répéter ce que vous demandez pour m'assurer de bien faire les choses:

Vous souhaitez extraire tous les enregistrements des 7 derniers jours à une date spécifiée?

Cela pourrait ressembler à:

select * from table where `date` between $date - interval 7 day and $date

Il est important de noter que $ date n'est pas une syntaxe mysql littérale mais juste un exemple d'espace réservé pour la date de début souhaitée. Si c'est pour le rapport, j'imagine que la requête sera finalement générée à partir d'un script? Si c'est vrai, il pourrait être plus simple dans cette langue, puis passer la valeur littérale dans le cadre de la requête construite.

Je suis fan de garder les requêtes aussi simples que possible, donc je laisserais ça comme ça. Je laisserai la place aux autres pour apporter des réponses afin d'accomplir ce que vous voulez dans une seule requête SQL-fu.

Modifier: Après avoir relu votre message, il semble que vous utilisiez probablement le type de date. Dans ce cas, le bloc suivant en italique peut être redondant. Je le laisse dans le cas où il serait utile aux autres (et puisque j'ai pris le temps de l'écrire :-)

Vous avez dit que vous utilisez un "horodatage"? Ce n'est pas techniquement un type de données Mysql. S'agit-il d'une date, d'un horodatage ou d'une date (l'heure et l'année existent également, mais d'après votre contexte, je pense que celles-ci ne s'appliquent pas)? Je demande parce que vous voudrez peut -être en faire juste une colonne de date au lieu des autres. Si c'est le bon choix pour vous, cela dépend vraiment des détails de la façon dont le columnn est utilisé et de la table interrogée. Si son seul but est simplement d'extraire des enregistrements dans une plage de dates, quelle que soit l'heure, la date est définitivement la voie à suivre. D'une part, cela ne nécessitait que 3 octets au lieu de 4 ou 8. Je peux expliquer d'autres raisons pour lesquelles vous souhaitez utiliser la date si elle correspond à vos besoins d'utilisation (c'est-à-dire que vous ne vous souciez pas de la portion de temps). Vous pouvez trouver des détails sur les différents types sur http://dev.mysql.com/doc/refman/5.0/en/datetime.html

http://dev.mysql.com/doc/refman/5.0/en/storage-requirements.html

Si et seulement si vous utilisez spécifiquement la date, vous pouvez envisager les éléments suivants:

Exécutez la requête avec le préfixe "expliquer". Des détails sur la façon d'interpréter la sortie et ce qui est le mieux peuvent être trouvés à http://dev.mysql.com/doc/refman/5.0/en/explain-output.html

Une fois que vous avez expliqué, modifiez la requête

select * from table where date in ("N", "N+1"..."N+7")

où vous énumérez toutes les dates individuelles qui vous intéressent. J'ai rencontré des situations où il est clair que MySQL n'est pas assez intelligent pour utiliser efficacement une requête de plage (c'est-à-dire entre x et y) vers des énumérations de valeurs spécifiques pour de petits ensembles.

Quel que soit le cas d'utilisation, vous voudrez vous assurer que la colonne est indexée si vous effectuez des requêtes de rapport régulières en fonction de ses valeurs.


1

M. @Rolando répond évidemment à la question mais je proposerai une autre décision qui vous permettra de manipuler et de personnaliser les calendriers entre les fuseaux horaires et le groupe le plus important par semaines définies dans différents fuseaux horaires

supposons donc que votre mySQL fonctionne sur un serveur configuré en UTC et que vous souhaitez avoir un calendrier personnalisé qui a 7 heures d'avance et que vos semaines devraient donc commencer le samedi 7 heures du matin.

CREATE TABLE `wh_blur_calendar` (
  `date` timestamp NOT NULL ,
  `y` smallint(6) DEFAULT NULL,
  `q` tinyint(4) DEFAULT NULL,
  `m` tinyint(4) DEFAULT NULL,
  `d` tinyint(4) DEFAULT NULL,
  `w` tinyint(4) DEFAULT NULL,
  PRIMARY KEY (`date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `wh_ints` (
  `i` tinyint(4) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into wh_ints values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);

maintenant une jointure cartésienne populaire qui devrait remplir votre table:

INSERT INTO wh_blur_calendar (date)
SELECT DATE('2010-01-01 00:00:00 ') + INTERVAL a.i*10000 + b.i*1000 + c.i*100 + d.i*10 + e.i DAY
FROM wh_ints a JOIN wh_ints b JOIN wh_ints c JOIN wh_ints d JOIN wh_ints e
WHERE (a.i*10000 + b.i*1000 + c.i*100 + d.i*10 + e.i) < 10245
ORDER BY 1;

Permet de mettre à jour les heures:

update  db_wh_repo.wh_blur_calendar set date = date_add(date, interval 7 hour);

et enfin organiser la semaine de votre calendrier de manière personnalisée

UPDATE wh_blur_calendar
SET 
    y = YEAR(date),
    q = quarter(date),
    m = MONTH(date),
    d = dayofmonth(date),
    w = week(date_add((date), interval 1 day));

Croyez-moi, je passe quelques heures à prendre cette décision, mais cela vous donne tellement de liberté au cas où vous souhaiteriez regrouper vos résultats en fonction d'un fuseau horaire et de définitions de semaine personnalisés.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.