Wie kann ich meine SQL-Anweisung mit wöchentlichen Ergebnissen verbessern, wobei die Woche am Donnerstag oder an einem anderen Wochentag beginnt?

8

Ich bin ein totaler Neuling, und ich konnte nirgendwo einen guten Weg finden, dies zu tun. Ich habe eine Datenbanktabelle, die Statistiken enthält, die zu verschiedenen Zeiten während der Woche aufgezeichnet werden. Die Berichtswoche beginnt am Donnerstag. Die Tabelle enthält eine Datenstempelspalte (Datum), in der gespeichert wird, wann die Daten aufgezeichnet wurden.

Ich muss die Daten für eine bestimmte Woche abrufen (Berichtswochen beginnen am Donnerstag). Ich habe folgende Abfrage geschrieben:

   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

Dies scheint bei ersten Tests zu funktionieren. Ich bin mir jedoch nicht sicher, ob dies der beste Weg ist. Ich weiß nicht viel über die MySQL-Leistung, aber es werden über hunderttausend Datensätze gefiltert. Wird diese Abfrage aufgrund der Anzahl der überprüften Bedingungen sehr langsam sein?

Die NOW () -Funktion wird verwendet, wenn der aktuellste Bericht abgerufen wird. In einigen Fällen muss ich jedoch Berichte für andere Wochen erstellen, damit ich ein anderes Datum an die Stelle setzen kann.

Wenn Sie dies auf diese Weise tun, müssen Sie die Abfrage neu schreiben, wenn sich die Berichtswoche ändert - beispielsweise ändert sich der Starttag auf Mittwoch.

Ich kann die WEEK () -Funktion nicht verwenden, da Sie damit nur eine Woche am Sonntag oder Montag beginnen können.

Alle Ideen zur Verbesserung dieser Abfrage werden sehr geschätzt!

Sonstige Hinweise: Derzeit wird MariaDB 5.3 verwendet.

James Lee Vann
quelle

Antworten:

5

Hier ist eine Abfrage, die ich geschrieben habe, um Ihnen den letzten Donnerstag und den letzten Mittwoch mitzuteilen

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;

Hier ist ein Beispiel für heute, den 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)

Ersetzen Sie einfach die NOW () - Funktionsaufrufe durch eine beliebige Datumszeit, und Sie haben die Woche ab Donnerstag immer für die von Ihnen gewählte Datumszeit.

Hier ist ein weiteres Beispiel mit dem spezifischen Datum '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)

Ihre tableheutige Referenzierungsanfrage würde ungefähr so ​​aussehen:

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;

Versuche es !!!

UPDATE 2011-09-22 16:27 EDT

Dies war meine vorgeschlagene Abfrage zum Markieren von Do-Mi.

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;

Wie wäre es mit anderen Wochen ???

  • (SELECT SUBSTR('6012345',wkndx,1) beginnt die Woche von Montag bis Sonntag?
  • (SELECT SUBSTR('5601234',wkndx,1) beginnt die Woche von Di bis Mo.
  • (SELECT SUBSTR('4560123',wkndx,1) beginnt die Woche ab Mi bis Di.
  • (SELECT SUBSTR('3456012',wkndx,1) beginnt die Woche von Do bis Mi.
  • (SELECT SUBSTR('2345601',wkndx,1) beginnt die Woche von Fr bis Do?
  • (SELECT SUBSTR('1234560',wkndx,1) beginnt die Woche ab Sa bis Fr.
  • (SELECT SUBSTR('0123456',wkndx,1) beginnt die Woche ab So bis Sa.
RolandoMySQLDBA
quelle
1

Lassen Sie mich noch einmal wiederholen, wonach Sie fragen, um sicherzustellen, dass ich alles richtig mache:

Sie möchten alle Datensätze der letzten 7 Tage an einem bestimmten Datum abrufen?

Es könnte so aussehen:

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

Es ist wichtig zu beachten, dass $ date keine wörtliche MySQL-Syntax ist, sondern nur ein Beispiel für einen Platzhalter für das gewünschte Startdatum. Wenn dies für die Berichterstellung ist, kann ich mir vorstellen, dass die Abfrage letztendlich aus einem Skript generiert wird. Wenn dies zutrifft, ist es in dieser Sprache möglicherweise einfacher und übergibt den Literalwert als Teil der erstellten Abfrage.

Ich bin ein Fan davon, Anfragen so einfach wie möglich zu halten, also würde ich es so lassen. Ich lasse anderen Raum, um Antworten beizutragen, um das zu erreichen, was Sie in einer einzigen SQL-Fu-Abfrage wollen.

Bearbeiten: Nach dem erneuten Lesen Ihres Beitrags scheinen Sie wahrscheinlich den Datumstyp zu verwenden. In diesem Fall kann der folgende Kursivschrift redundant sein. Ich lasse es für den Fall, dass es für andere hilfreich ist (und da ich mir die Zeit genommen habe, es zu schreiben :-)

Sie sagten, Sie verwenden einen "Datenstempel"? Das ist technisch gesehen kein MySQL-Datentyp. Ist es ein Datum, eine Uhrzeit, ein Zeitstempel oder ein Datum (Uhrzeit und Jahr existieren ebenfalls, aber aus Ihrem Kontext denke ich, dass diese nicht zutreffen)? Ich frage, weil Sie es vielleicht nur eine Datumsspalte anstelle der anderen machen möchten. Ob dies die richtige Wahl für Sie ist, hängt wirklich von den Details ab, wie die Spalte verwendet wird und die Tabelle über alle abgefragt wird. Wenn es nur darum geht, Datensätze in einem Datumsbereich unabhängig von der Uhrzeit abzurufen, ist das Datum definitiv der richtige Weg. Zum einen waren nur 3 Bytes anstelle von 4 oder 8 erforderlich. Ich kann auf andere Gründe eingehen, aus denen Sie das Datum verwenden möchten, wenn es Ihren Nutzungsanforderungen entspricht (dh Sie interessieren sich nicht für den Zeitanteil). Details zu den verschiedenen Typen finden Sie unter http://dev.mysql.com/doc/refman/5.0/en/datetime.html

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

Wenn und nur wenn Sie das Datum speziell verwenden, können Sie Folgendes berücksichtigen:

Führen Sie die Abfrage mit dem Präfix "EXPLAIN" aus. Details zur Interpretation der Ausgabe und zu den besten finden Sie unter http://dev.mysql.com/doc/refman/5.0/en/explain-output.html

Sobald Sie diese Erklärung haben, ändern Sie die Abfrage in

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

Ich bin auf Situationen gestoßen, in denen klar ist, dass MySQL nicht intelligent genug ist, um effektiv eine Bereichsabfrage (dh zwischen x und y) zu verwenden, die bestimmte Werte für kleine Mengen auflistet.

Unabhängig vom Anwendungsfall möchten Sie sicherstellen, dass die Spalte indiziert ist, wenn Sie regelmäßig Berichtsabfragen basierend auf ihren Werten durchführen.

atxdba
quelle
1

Herr @Rolando beantwortet natürlich die Frage, aber ich werde eine andere Entscheidung vorschlagen, mit der Sie Kalender zwischen Zeitzonen und der wichtigsten Gruppe nach Wochen bearbeiten und anpassen können, die in verschiedenen Zeitzonen definiert sind

Nehmen wir also an, dass mySQL auf einem UTC-konfigurierten Server ausgeführt wird und Sie einen benutzerdefinierten Kalender haben möchten, der 7 Stunden im Voraus liegt. Daher sollten Ihre Wochen am Samstag um 7 Uhr beginnen

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

Jetzt ein beliebter kartesischer Join, der Ihre Tabelle füllen sollte:

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;

Aktualisieren wir die Stunden:

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

und ordnen Sie schließlich die Woche Ihres Kalenders auf benutzerdefinierte Weise

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

Glauben Sie mir, ich verbringe einige Stunden damit, diese Entscheidung zu treffen, aber es gibt Ihnen so viel Freiheit, falls Sie Ihre Ergebnisse basierend auf einer benutzerdefinierten Zeitzone und Wochendefinitionen gruppieren möchten.

Ninjabber
quelle