Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Obtenha os registros do mês passado no servidor SQL


Todas as respostas existentes (de trabalho) têm um dos dois problemas:
  1. Eles ignorarão os índices na coluna pesquisada
  2. O irá (potencialmente) selecionar dados não pretendidos, corrompendo silenciosamente seus resultados.

1. Índices ignorados:

Na maioria das vezes, quando uma coluna que está sendo pesquisada tem uma função chamada nela (incluindo implicitamente, como para CAST ), o otimizador deve ignorar os índices na coluna e pesquisar em todos os registros. Aqui está um exemplo rápido:

Estamos lidando com timestamps, e a maioria dos RDBMSs tende a armazenar essas informações como um valor crescente de algum tipo, geralmente um long ou BIGINTEGER contagem de mili-/nanossegundos. A hora atual parece/é armazenada assim:
1402401635000000  -- 2014-06-10 12:00:35.000000 GMT

Você não vê o valor 'Ano' ('2014' ) lá, não é? Na verdade, há um pouco de matemática complicada para traduzir para frente e para trás. Portanto, se você chamar qualquer uma das funções de extração/data na coluna pesquisada, o servidor terá que realizar toda essa matemática apenas para descobrir se você pode incluí-la nos resultados. Em tabelas pequenas, isso não é um problema, mas à medida que a porcentagem de linhas selecionadas diminui, isso se torna um dreno cada vez maior. Então, neste caso, você está fazendo isso uma segunda vez para perguntar sobre MONTH ... bem, você entendeu.

2. Dados não intencionais:

Dependendo da versão específica do SQL Server e dos tipos de dados da coluna, usando BETWEEN (ou intervalos de limite superior inclusivos semelhantes:<= ) pode resultar na seleção de dados errados. Essencialmente, você potencialmente acaba incluindo dados da meia-noite do dia "próximo" ou excluindo alguma parte dos registros do dia "atual".

O que você deveria estar fazendo:

Portanto, precisamos de uma maneira segura para nossos dados e usaremos índices (se viável). A forma correta é então da forma:
WHERE date_created >= @startOfPreviousMonth AND date_created < @startOfCurrentMonth

Dado que há apenas um mês, @startOfPreviousMonth pode ser facilmente substituído/derivado por:
DATEADD(month, -1, @startOCurrentfMonth)

Se você precisar derivar o início do mês atual no servidor, poderá fazê-lo através do seguinte:
DATEADD(month, DATEDIFF(month, 0, CURRENT_TIMESTAMP), 0)

Uma palavra rápida de explicação aqui. O DATEDIFF(...) inicial obterá a diferença entre o início da era atual (0001-01-01 - AD, CE, o que for), essencialmente retornando um número inteiro grande. Esta é a contagem de meses para o início do atual mês. Em seguida, adicionamos esse número ao início da era, que está no início de um determinado mês.

Portanto, seu script completo pode/deve ser semelhante ao seguinte:
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

Todas as operações de data são, portanto, executadas apenas uma vez, em um valor; o otimizador é livre para usar índices e nenhum dado incorreto será incluído.