2009-07-07 7 views
0

Ich bin auf der Suche nach einem Best-Practice-Ratschlag, wie Sie Abfragen beschleunigen und gleichzeitig den Overhead minimieren können, der zum Aufrufen von date/mktime-Funktionen benötigt wird. Verharmlosen das Problem, das ich mit der folgenden Tabelle Layout zu tun habe:Wie kann die Belastung in Abfragen minimiert werden, die eine Gruppierung mit unterschiedlichen Intervallen erfordern?

CREATE TABLE my_table(
    id INTEGER PRIMARY KEY NOT NULL AUTO_INCREMENT, 
    important_data INTEGER, 
    date INTEGER); 

Der Benutzer kann wählen 1 zeigen) alle Einträge zwischen zwei Daten:

SELECT * FROM my_table 
    WHERE date >= ? AND date <= ? 
    ORDER BY date DESC; 

Ausgang:

10-21-2009 12:12:12, 10002 
10-21-2009 14:12:12, 15002 
10-22-2009 14:05:01, 20030 
10-23-2009 15:23:35, 300 
.... 

Ich glaube nicht, dass es in diesem Fall viel zu verbessern gibt.

2) Summarize/Gruppe die Ausgabe von Tag, Woche, Monat, Jahr:

SELECT COUNT(*) AS count, SUM(important_data) AS important_data 
    FROM my_table 
    WHERE date >= ? AND date <= ? 
    ORDER BY date DESC; 

Beispiel Ausgabe von Monat:

10-2009, 100002 
11-2009, 200030 
12-2009, 3000 
01-2010, 0 /* <- very important to show empty dates, with no entries in the table! */ 
.... 

Um das zu erreichen Option 2) ich zur Zeit ein laufenden bin sehr kostspielige for-Schleife mit mktime/Datum wie folgend:

for(...){ /* example for group by day */ 
    $span_from = (int)mktime(0, 0, 0, date("m", $time_min), date("d", $time_min)+$i, date("Y", $time_min)); 
    $span_to = (int)mktime(0, 0, 0, date("m", $time_min), date("d", $time_min)+$i+1, date("Y", $time_min)); 
    $query = ".."; 
    $output = date("m-d-y", ..); 
} 

Was sind meine Ideen bis jetzt? Fügen Sie zusätzliche/redundante Spalten (INTEGER) für Tag (20091212), Monat (200912), Woche (200942) und Jahr (2009) hinzu. Auf diese Weise kann ich alle unnötigen Abfragen in der for-Schleife loswerden. Allerdings habe ich immer noch das Problem, sehr schnell alle Daten zu berechnen, die keine Entsprechung in der Datenbank haben. Eine Möglichkeit, das Problem einfach zu verschieben, besteht darin, MySQL die Aufgabe übernehmen zu lassen und einfach eine große Abfrage (alle Daten berechnen/MySQL-Datumsfunktionen verwenden) mit einer linken Verknüpfung (den Daten) zu verwenden. Wäre es klug, MySQL die Extralast zu überlassen? Jedenfalls zögere ich, all diese mktime/dates in der for-Schleife zu verwenden. Da ich die volle Kontrolle über das Layout und den Code habe, sind auch Vorschläge mit großen Änderungen willkommen!

aktualisieren

Dank Greg kam ich auf die folgende SQL-Abfrage auf. es nervt ich jedoch immer noch 50 Zeilen von SQL-Anweisungen verwenden - mit PHP aufzubauen - das vielleicht schneller getan werden könnte und eleganter sonst:

SELECT * FROM ( 
    SELECT DATE_ADD('2009-01-30', INTERVAL 0 DAY) AS day UNION ALL 
    SELECT DATE_ADD('2009-01-30', INTERVAL 1 DAY) AS day UNION ALL 
    SELECT DATE_ADD('2009-01-30', INTERVAL 2 DAY) AS day UNION ALL 
    SELECT DATE_ADD('2009-01-30', INTERVAL 3 DAY) AS day UNION ALL 
    ...... 
    SELECT DATE_ADD('2009-01-30', INTERVAL 50 DAY) AS day) AS dates 
LEFT JOIN (
    SELECT DATE_FORMAT(date, '%Y-%m-%d') AS date, SUM(data) AS data 
    FROM test 
    GROUP BY date 
) AS results 
ON DATE_FORMAT(dates.day, '%Y-%m-%d') = results.date; 

Antwort

1

Sie sollten auf keinen Fall eine Abfrage innerhalb einer Schleife tun. Sie können Gruppe wie folgt aus:

SELECT COUNT(*) AS count, SUM(important_data) AS important_data, DATE_FORMAT('%Y-%m', date) AS month 
    FROM my_table 
    WHERE date BETWEEN ? AND ? -- This should be the min and max of the whole range 
    GROUP BY DATE_FORMAT('%Y-%m', date) 
    ORDER BY date DESC; 

Dann werden diese nach Datum und Schleife über Ihre Daten eingegeben in ein Array ziehen reichen, wie Sie tun (die Schleife sollte ziemlich Licht auf CPU sein).

+0

Danke für die schnelle Antwort! Ich werde einige Zeit brauchen, um Ihre Idee zu testen, da ich von INTEGER zu DATETIME wechseln muss, um DATE_FORMAT erfolgreich zu verwenden. Es sieht jedoch vielversprechend aus. Wenn ich mich nicht irre, fehlt eine Sache, wie bekomme ich all diese Daten ohne Einträge in der Tabelle? Zum Beispiel: Ich habe Daten für 2009-10 und 2009-11, aber 2009-12 ist leer, sollte aber dennoch sichtbar sein ..Soll ich dafür auf die PHP-Funktionalität zurückgreifen oder gibt es dafür eine elegante mysql-Lösung? – merkuro

+0

@merkuro: Sie können nur Daten ausgeben, die * da * sind. Fehlende Aufzeichnungen werden nicht magisch erscheinen. Wenn Sie für jeden Tag des Jahres eine Aufzeichnung benötigen, erstellen Sie eine Kalendertabelle und machen Sie eine äußere Verknüpfung. Eine solche Tabelle muss nur einmal erstellt/gefüllt werden und benötigt nur 365 Zeilen pro Jahr. Auf diese Weise würden irgendwelche "Lücken" in der Ausgabe verschwinden. – Tomalak

+0

@Tomalak Sie meinen, dass MySQL-Installationen nicht mit einem integrierten Magier ausgestattet sind? Ehrlich, welchen Zweck hatte dein snarky Kommentar? Ich habe bereits die Option zur Verwendung eines Links Joins erwähnt, obwohl ich in dieser Lösung aufgrund der erhöhten Datenträgerauslastung ein mögliches Leistungsproblem sehe. Wie auch immer, ich hoffte, dass jemand mit einer netten Range/Array-Funktion kommen würde, die ich übersehen habe. – merkuro