Come posso migliorare la mia istruzione SQL con risultati settimanali con settimana a partire da giovedì o in qualsiasi altro giorno della settimana?


8

Sono un principiante assoluto e non sono riuscito a trovare un buon modo per farlo da nessuna parte. Ho una tabella di database che contiene statistiche che vengono registrate in vari momenti della settimana. La settimana di riferimento inizia giovedì. La tabella contiene una colonna datestamp (data) che memorizza quando sono stati registrati i dati.

Devo estrarre i dati per una determinata settimana (le settimane di segnalazione iniziano il giovedì). Ho scritto la seguente query:

   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

Questo sembra funzionare sui test iniziali. Ma non sono sicuro che sia il modo migliore per farlo. Non so molto sulle prestazioni di MySQL, ma ci saranno oltre centomila record da filtrare. Questa query sarà molto lenta a causa del numero di condizioni verificate?

La funzione NOW () viene utilizzata per estrarre il rapporto più recente, tuttavia, in alcuni casi dovrò fare rapporti per altre settimane, in modo da sostituire un'altra data nel luogo.

Inoltre, in questo modo è necessario riscrivere la query se la settimana del rapporto cambia, ad esempio il giorno di inizio cambia a mercoledì.

Non riesco a utilizzare la funzione WEEK () perché puoi iniziare solo una settimana su Sun o Lun con essa.

Tutte le idee per migliorare questa query sono molto apprezzate!

Altre note: attualmente in uso MariaDB 5.3.

Risposte:


5

Ecco una domanda che ho scritto per darti l'ultimo giovedì e il mercoledì finale

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;

Ecco un esempio per oggi, 21/09/2011

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)

Sostituisci semplicemente le chiamate della funzione NOW () con qualsiasi datetime che desideri e avrai la settimana che inizia giovedì tutto il tempo per il datetime che scegli.

Ecco un altro esempio utilizzando la data specifica "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)

La tua query di tableriferimento oggi assomiglierebbe a qualcosa del genere:

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;

Provaci !!!

AGGIORNAMENTO 2011-09-22 16:27 EDT

Questa è stata la mia proposta di interrogazione per contrassegnare Gio-Mer.

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;

Che ne dici di altre settimane ???

  • (SELECT SUBSTR('6012345',wkndx,1) fa la settimana a partire dal lunedì alla fine del sole
  • (SELECT SUBSTR('5601234',wkndx,1) fa la settimana che inizia mar e termina lun
  • (SELECT SUBSTR('4560123',wkndx,1) fa la settimana che inizia mer e finisce mar
  • (SELECT SUBSTR('3456012',wkndx,1) la settimana che inizia giovedi termina mer
  • (SELECT SUBSTR('2345601',wkndx,1) fa la settimana che inizia venerdì e finisce gio
  • (SELECT SUBSTR('1234560',wkndx,1) la settimana che inizia sabato termina venerdì
  • (SELECT SUBSTR('0123456',wkndx,1) fa la settimana che inizia domenica e finisce sabato

1

Permettimi di riaffermare ciò che stai chiedendo per assicurarmi di aver capito bene:

Desideri estrarre tutti i record degli ultimi 7 giorni in una data specifica?

Potrebbe apparire come:

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

È importante notare che $ date non è una sintassi mysql letterale ma solo un segnaposto di esempio per la data di inizio desiderata. Se questo è per la segnalazione, immagino che la query verrà generata in ultima analisi da alcuni script? Se questo è vero, potrebbe essere più semplice in quella lingua e quindi passare il valore letterale come parte della query costruita.

Sono un fan di mantenere le domande il più semplice possibile, quindi lo lascerei così. Lascio spazio agli altri per fornire risposte per realizzare ciò che desideri in una singola query SQL-fu.

Modifica: dopo aver riletto il tuo post sembra che tu stia probabilmente utilizzando il tipo di data. Nel qual caso il seguente blocco in corsivo può essere ridondante. Lo lascio nel caso in cui sia utile agli altri (e da quando ho preso il tempo di scriverlo :-)

Hai detto che stai usando un "datestamp"? Questo non è tecnicamente un tipo di dati Mysql. È un datetime, un timestamp o una data (esistono anche ora e anno ma dal tuo contesto ritengo che questi non siano applicabili)? Lo chiedo perché si potrebbe desiderare di fare solo una colonna di data invece degli altri. Se questa è la scelta giusta per te dipende davvero dai dettagli di come viene utilizzato il columnn e dalla query su cui si esegue la query. Se il suo unico scopo è solo quello di estrarre i record in un intervallo di date, indipendentemente dall'ora, allora la data è sicuramente la strada da percorrere. Per uno, ha richiesto solo 3 byte invece di 4 o 8. Posso approfondire altri motivi per cui vorresti usare la data se soddisfa i tuoi requisiti di utilizzo (cioè non ti interessa la parte temporale). Puoi trovare dettagli sui diversi tipi su http://dev.mysql.com/doc/refman/5.0/en/datetime.html

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

Se e solo se si sta utilizzando la data in modo specifico, è possibile considerare quanto segue:

Esegui la query con il prefisso "Spiega". I dettagli su come interpretare l'output e cosa è meglio sono disponibili all'indirizzo http://dev.mysql.com/doc/refman/5.0/en/explain-output.html

Una volta che hai spiegato, cambia la query

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

in cui elenchi tutte le singole date a cui sei interessato. Mi sono imbattuto in situazioni in cui è chiaro che MySQL non è abbastanza intelligente da utilizzare in modo efficace una query di intervallo (ovvero tra xey) rispetto a elenchi di valori specifici per piccoli set.

Qualunque sia il caso d'uso, ti consigliamo di assicurarti che la colonna sia indicizzata se esegui query di report regolari in base ai suoi valori.


1

Mr @Rolando risponde ovviamente alla domanda, ma proporrò un'altra decisione che ti permetterà di manipolare e personalizzare i calendari tra fusi orari e il gruppo più importante per settimane che sono definiti in diversi fusi orari

quindi, supponiamo che mySQL sia in esecuzione sul server configurato UTC e che tu voglia avere un calendario personalizzato con 7 ore di anticipo e quindi le tue settimane dovrebbero iniziare sabato 7

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);

ora un popolare join cartesiano che dovrebbe popolare il tuo tavolo:

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;

Consente di aggiornare le ore:

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

e infine organizza la settimana del tuo calendario in modo personalizzato

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));

Credimi, passo alcune ore a prendere questa decisione, ma ti dà così tanta libertà nel caso in cui desideri raggruppare i risultati in base a un fuso orario personalizzato e definizioni di settimana.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.