Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Ottieni i record del mese scorso nel server SQL

Tutte le risposte esistenti (funzionanti) presentano uno di due problemi:

  1. Ignoreranno gli indici sulla colonna cercata
  2. La volontà (potenzialmente) selezionerà dati non previsti, danneggiando silenziosamente i tuoi risultati.

1. Indici ignorati:

Per la maggior parte, quando una colonna in fase di ricerca ha una funzione chiamata su di essa (incluso implicitamente, come per CAST ), l'ottimizzatore deve ignorare gli indici sulla colonna e cercare in ogni record. Ecco un rapido esempio:

Abbiamo a che fare con timestamp e la maggior parte degli RDBMS tende a memorizzare queste informazioni come un valore crescente di qualche tipo, di solito un long o BIGINTEGER conteggio di milli-/nanosecondi. L'ora corrente appare/viene quindi memorizzata in questo modo:

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

Non vedi il valore 'Anno' ('2014' ) lì dentro, vero? In effetti, c'è un bel po' di matematica complicata da tradurre avanti e indietro. Quindi, se chiami una qualsiasi delle funzioni di estrazione/parte della data nella colonna cercata, il server deve eseguire tutta quella matematica solo per capire se puoi includerla nei risultati. Sulle tabelle piccole questo non è un problema, ma quando la percentuale di righe selezionate diminuisce, questo diventa uno scarico sempre più grande. Quindi, in questo caso, lo stai facendo una seconda volta per aver chiesto informazioni su MONTH ... beh, hai capito.

2. Dati non intenzionali:

A seconda della versione particolare di SQL Server e dei tipi di dati delle colonne, utilizzando BETWEEN (o intervalli di limite superiore inclusi simili:<= ) può comportare la selezione di dati errati. In sostanza, potresti potenzialmente includere i dati dalla mezzanotte del giorno "successivo" o escludere una parte dei record del giorno "corrente".

Cosa dovresti fare:

Quindi abbiamo bisogno di un modo che sia sicuro per i nostri dati e utilizzerà gli indici (se praticabili). Il modo corretto è quindi della forma:

WHERE date_created >= @startOfPreviousMonth AND date_created < @startOfCurrentMonth

Dato che c'è solo un mese, @startOfPreviousMonth può essere facilmente sostituito/derivato da:

DATEADD(month, -1, @startOCurrentfMonth)

Se devi ricavare l'inizio del mese corrente nel server, puoi farlo tramite:

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

Una breve parola di spiegazione qui. Il DATEDIFF(...) iniziale otterrà la differenza tra l'inizio dell'era corrente (0001-01-01 - AD, CE, qualunque cosa), restituendo essenzialmente un numero intero grande. Questo è il conteggio dei mesi dall'inizio della corrente mese. Quindi aggiungiamo questo numero all'inizio dell'era, che è all'inizio del mese specificato.

Quindi il tuo script completo potrebbe/dovrebbe essere simile al seguente:

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

Tutte le operazioni sulla data vengono quindi eseguite una sola volta, su un valore; l'ottimizzatore è libero di utilizzare gli indici e non verranno inclusi dati errati.