sql >> Databáze >  >> RDS >> Sqlserver

Získejte záznamy za minulý měsíc na SQL serveru

Všechny existující (pracovní) odpovědi mají jeden ze dvou problémů:

  1. Budou ignorovat indexy v prohledávaném sloupci
  2. Ten (potenciálně) vybere data, která nejsou zamýšlena, a tiše poškodí vaše výsledky.

1. Ignorované indexy:

Z velké části platí, že když má prohledávaný sloupec na sobě zavolanou funkci (včetně implicitně, jako pro CAST ), musí optimalizátor ignorovat indexy ve sloupci a prohledávat každý záznam. Zde je rychlý příklad:

Máme co do činění s časovými razítky a většina RDBMS má tendenci ukládat tyto informace jako rostoucí hodnotu nějakého druhu, obvykle long nebo BIGINTEGER počet mili-/nanosekund. Aktuální čas tedy vypadá/je uložen takto:

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

Nevidíte hodnotu 'Rok' ('2014' ) tam, ano? Ve skutečnosti je tu poměrně složitá matematika k překládání tam a zpět. Pokud tedy zavoláte některou z funkcí extrakce/datové části na hledaný sloupec, server musí provést veškerou matematiku, aby zjistil, zda ji můžete zahrnout do výsledků. Na malých stolech to není problém, ale jak se procento vybraných řádků snižuje, dochází k většímu a většímu odlivu. Pak v tomto případě to děláte podruhé, protože se ptáte na MONTH ... no, chápete.

2. Nezamýšlená data:

V závislosti na konkrétní verzi SQL Serveru a datových typech sloupců pomocí BETWEEN (nebo podobné rozsahy včetně horních hranic:<= ) může mít za následek výběr nesprávných dat. V podstatě můžete skončit zahrnutím dat z půlnoci „následujícího“ dne nebo vyloučením určité části záznamů „aktuálního“ dne.

Co byste měli dělat:

Potřebujeme tedy způsob, který je bezpečný pro naše data a bude používat indexy (pokud je to možné). Správný způsob je pak ve tvaru:

WHERE date_created >= @startOfPreviousMonth AND date_created < @startOfCurrentMonth

Vzhledem k tomu, že existuje pouze jeden měsíc, @startOfPreviousMonth lze snadno nahradit/odvodit:

DATEADD(month, -1, @startOCurrentfMonth)

Pokud potřebujete odvodit začátek aktuálního měsíce na serveru, můžete to udělat takto:

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

Zde je rychlé vysvětlení. Počáteční DATEDIFF(...) získá rozdíl mezi začátkem aktuální éry (0001-01-01 - AD, CE, cokoliv), v podstatě vrací velké celé číslo. Toto je počet měsíců do začátku aktuálního Měsíc. Toto číslo pak přičteme k začátku éry, který je na začátku daného měsíce.

Váš úplný skript by tedy mohl/měl by vypadat podobně jako následující:

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

Všechny operace s datem jsou tak provedeny pouze jednou, na jedné hodnotě; optimalizátor může volně používat indexy a nebudou zahrnuta žádná nesprávná data.



  1. CHYBA 1148:Použitý příkaz není u této verze MySQL povolen

  2. Stav zobrazení MySQL – aktivní nebo celkový počet připojení?

  3. Získávání ORA-03115:Nepodporovaný datový typ sítě nebo chyba reprezentace při načítání pole varchar z anonymního pl/sql

  4. Entity Framework vytvoří název tabulky v množném čísle, ale pohled očekává singulární název tabulky?