2010-02-24 5 views
5

ich ein paar timestamped Zeilen (die ‚Datetime‘ Datentyp)select * from table where datetime in Monat (ohne Index zu brechen)

ich alle Zeilen auswählen möchten, die einen Zeitstempel haben also innerhalb ein bestimmter Monat.

Die Spalte ist indiziert, so dass ich MONTH (timestamp) = 3 nicht ausführen kann, da dies diesen Index unbrauchbar macht.

Wenn ich Jahr und Monat Variablen (in Perl), gibt es ein schrecklich bisschen SQL Ich mag verwenden:

timestamp BETWEEN DATE($year, $month, 0) AND DATE($year, $month, 31); 

Aber schöner, und tatsächlich funktioniert?

+0

Darf ich annehmen, dass Sie alle Zeilen eines bestimmten Monat über _alle_ Jahren wollen? – Arthur

+0

Ein bestimmter einzelner Monat. – aidan

Antwort

11

Ich würde eigentlich mit der Idee gehen, die Sie vorgeschlagen haben; vielleicht mit einem kleinen Unterschied:

select * 
from your_table 
where date_field >= '2010-01-01' 
    and date_field < '2010-02-01' 

(natürlich an Ihnen die Verwendung $year und $month richtig)


Notiere die < '2010-02-01' Teil: Sie könnte dies zu berücksichtigen haben, wenn Sie Daten, die die Zeit enthalten.

Zum Beispiel, wenn Sie eine Zeile mit einem Datum wie '2010-01-31 12:53:12' haben, werden Sie wahrscheinlich, dass Linie ausgewählt haben wollen - und in der Standardeinstellung bedeutet '2010-01-31''2010-01-31 00:00:00'.


Vielleicht sieht das nicht "nett" für das Auge aus; aber es wird funktionieren; und benutze den Index ... Es ist die Art von Lösung, die ich normalerweise benutze, wenn ich dieses Problem habe.

+0

+1 für den Vorschlag eines halboffenen Intervalls. – dalle

1

Wenn Sie den gleichen Monat eines jeden Jahres müssen die Index, den Sie haben, werden Sie und keine Menge von SQL-Syntax Tricks Sie nicht helfen

Auf der anderen Seite wird helfen, wenn Sie einen Monat eines Jahres müssen dann jede Abfrage mit Datumsbereichen sollte es tun

1

Eine weitere Alternative ist das Hinzufügen einer zusätzlichen Spalte zu der Tabelle, die den Monat vorberechnet speichert. Das wäre nur eine einfache int-Spalte, und es ist trivial zu indexieren. Wenn Sie keine kajillion Zeilen haben, ist der zusätzliche Platz für ein unsigniertes tiny int vernachlässigbar (ein Byte + db Overhead pro Zeile).

Es würde ein bisschen mehr Arbeit erfordern, um mit der Timestamp-Spalte synchronisiert zu bleiben, aber dafür gibt es Trigger.

2

Dies ist substanziell Pascal MARTIN answer, vermeidet aber wissen zu müssen, was ausdrücklich im nächsten Jahr/Monat (also müssen Sie nicht Jahr erhöhen und wickeln rund um die $month, wenn $month == 12):

my $sth = $mysql_dbh->prepare(<<__EOSQL); 
SELECT ... 
    FROM tbl 
WHERE ts >= ? AND ts < (? + INTERVAL 1 MONTH) 
__EOSQL 

my $yyyymm = $year . '-' . sprintf('%02d', $month); 

$sth->execute($yyyymm, $yyyymm); 

Für fugly Punkte Bonus, können Sie auch dies tun:

... WHERE ts BETWEEN ? AND (? + INTERVAL 1 MONTH - INTERVAL 1 SECOND) 

die - INTERVAL 1 SECOND wird die obere Grenze von einem Datum in einen DATETIME-/TIMESTAMP-Typen bis zur letzten Sekunde der Anzeige eingestellt coerce ay, was, wie Pascal andeutet, was Sie an der Obergrenze wollen.

+0

Ich mag es! ..... – aidan

-1

Wie wäre es WHERE MONTH(`date`) = '$month' AND YEAR(`date`) = '$year'

+2

nein. das würde den Index ungültig machen. – aidan

Verwandte Themen