sql >> Base de Datos >  >> RDS >> Sqlserver

Obtenga los registros del mes pasado en el servidor SQL

Todas las respuestas existentes (en funcionamiento) tienen uno de dos problemas:

  1. Ignorarán los índices en la columna que se está buscando
  2. Seleccionará (potencialmente) datos que no están previstos, corrompiendo silenciosamente sus resultados.

1. Índices ignorados:

En su mayor parte, cuando una columna que se busca tiene una función llamada (incluso implícitamente, como para CAST ), el optimizador debe ignorar los índices de la columna y buscar en todos los registros. He aquí un ejemplo rápido:

Estamos tratando con marcas de tiempo, y la mayoría de los RDBMS tienden a almacenar esta información como un valor creciente de algún tipo, generalmente un long o BIGINTEGER conteo de mili-/nanosegundos. La hora actual se ve/se almacena así:

1402401635000000  -- 2014-06-10 12:00:35.000000 GMT

No ve el valor 'Año' ('2014' ) ahí dentro, ¿verdad? De hecho, hay un poco de matemáticas complicadas para traducir de un lado a otro. Entonces, si llama a cualquiera de las funciones de parte de extracción/fecha en la columna buscada, el servidor tiene que realizar todos esos cálculos solo para averiguar si puede incluirla en los resultados. En las tablas pequeñas, esto no es un problema, pero a medida que disminuye el porcentaje de filas seleccionadas, se convierte en un drenaje cada vez mayor. Entonces, en este caso, lo estás haciendo por segunda vez para preguntar sobre MONTH ... bueno, te haces una idea.

2. Datos no deseados:

Dependiendo de la versión particular de SQL Server y los tipos de datos de columna, usando BETWEEN (o rangos superiores inclusivos similares:<= ) puede resultar en la selección de datos incorrectos. Esencialmente, podría terminar incluyendo datos de la medianoche del día "siguiente" o excluyendo una parte de los registros del día "actual".

Lo que debería estar haciendo:

Entonces, necesitamos una forma que sea segura para nuestros datos y use índices (si es viable). La forma correcta es entonces de la forma:

WHERE date_created >= @startOfPreviousMonth AND date_created < @startOfCurrentMonth

Dado que solo hay un mes, @startOfPreviousMonth puede ser fácilmente sustituido por/derivado por:

DATEADD(month, -1, @startOCurrentfMonth)

Si necesita derivar el inicio del mes actual en el servidor, puede hacerlo a través de lo siguiente:

DATEADD(month, DATEDIFF(month, 0, CURRENT_TIMESTAMP), 0)

Una palabra rápida de explicación aquí. El DATEDIFF(...) inicial obtendrá la diferencia entre el inicio de la era actual (0001-01-01 - AD, CE, lo que sea), esencialmente devolviendo un número entero grande. Esta es la cuenta de meses hasta el inicio de la actual mes. Luego agregamos este número al comienzo de la era, que es al comienzo del mes dado.

Por lo tanto, su secuencia de comandos completa podría/debería ser similar a la siguiente:

DECLARE @startOfCurrentMonth DATETIME
SET @startOfCurrentMonth = DATEADD(month, DATEDIFF(month, 0, CURRENT_TIMESTAMP), 0)

SELECT *
FROM Member
WHERE date_created >= DATEADD(month, -1, @startOfCurrentMonth) -- this was originally    misspelled
      AND date_created < @startOfCurrentMonth

Por lo tanto, todas las operaciones de fecha se realizan solo una vez, en un valor; el optimizador es libre de usar índices y no se incluirán datos incorrectos.