2010-02-24 22 views
5

Tengo un montón de filas con sellos de tiempo (utilizando el tipo de datos 'fecha y hora')seleccionar * de la tabla donde fecha y hora en el mes (sin romper índice)

Quiero seleccionar todas las filas que tienen una marca de tiempo que está dentro de un mes en particular

La columna está indexada, por lo que no puedo hacer MONTH (timestamp) = 3 porque eso hará que este índice sea inutilizable.

Si tengo año y mes variables (en Perl), ¿hay algo horrible de SQL que puede utilizar como:

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

Pero más agradable, y realmente funciona?

+0

¿Puedo asumir que desea que todas las filas de un determinado mes durante _todas_ años? – Arthur

+0

Un solo mes en particular. – aidan

Respuesta

11

De hecho, me iría con la idea que usted propuso; tal vez con una pequeña diferencia:

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

(Por supuesto, depende de usted el uso $year y $month adecuadamente)


Nota la parte < '2010-02-01': puede que tenga que tener en cuenta esto, si usted tiene fechas que incluyen el tiempo.

Por ejemplo, si usted tiene una línea con una fecha como '2010-01-31 12:53:12', es probable que desee tener esa línea seleccionada - y, por defecto, '2010-01-31' significa '2010-01-31 00:00:00'.


Tal vez eso no se ve 'bien' a la vista; pero funcionará; y use el índice ... Es el tipo de solución que generalmente uso cuando tengo ese tipo de problema.

+0

+1 por sugerir un intervalo medio abierto. – dalle

1

Si necesita el mismo mes de cada año el índice que tiene no le ayudará y ninguna cantidad de engaños sintaxis SQL le ayudará a

Por otro lado, si usted necesita un mes de un año en particular, entonces cualquier consulta con rangos de fechas debe hacerlo

1

Otra alternativa es agregar una columna adicional a la tabla que almacena el mes, precalculada. Eso sería una simple columna int, y es trivial de indexar. A menos que tenga que lidiar con filas de kajillion, el espacio extra para una int diminuta sin firmar es insignificante (un byte + carga por sobre de db por fila).

Necesitaría un poco de trabajo extra para mantener la sincronización con la columna de marca de tiempo, pero para eso están los disparadores.

2

Esta es sustantivamente Pascal Martin answer, pero evita tener que conocer explícitamente lo/mes es el próximo año (por lo que no tiene que incrementar año y envolver alrededor de la $month, cuando $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); 
para

bonificación fugly puntos, también se puede hacer esto:

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

Eso - INTERVAL 1 SECOND se coaccionar el límite superior de una fecha en un tipo DATETIME/TIMESTAMP establecido en el último segundo de publicidad ay, que es, como lo indicó Pascal, lo que quieres en el límite superior.

+0

¡Me gusta! ..... – aidan

-1

¿Qué tal WHERE MONTH(`date`) = '$month' AND YEAR(`date`) = '$year'

+2

no. eso invalidaría el índice. – aidan

Cuestiones relacionadas