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

SQL Server MEZI

Vymyslet způsob, jak používat BETWEEN s tabulkou tak, jak je, bude fungovat, ale v každém případě bude mít horší výkon:

  • Namísto toho, abychom s nimi pracovali jako s daty, bude to přinejlepším spotřebovávat více procesoru, aby bylo možné provést nějaké výpočty na řádcích.
  • V nejhorším případě to vynutí prohledávání tabulky na každém řádku v tabulce, ale pokud mají vaše sloupce indexy, je hledání možné pomocí správného dotazu. To by mohl být OBROVSKÝ rozdíl ve výkonu, protože vynucení omezení do klauzule BETWEEN zakáže používání indexu.

Pokud máte ve sloupcích s datem index a vůbec vám záleží na výkonu, doporučuji následující:

DECLARE
   @FromDate date = '20111101',
   @ToDate date = '20120201';

SELECT *
FROM dbo.YourTable T
WHERE
   (
      T.[Year] > Year(@FromDate)
      OR (    
         T.[Year] = Year(@FromDate)
         AND T.[Month] >= Month(@FromDate)
      )
   ) AND (
      T.[Year] < Year(@ToDate)
      OR (
         T.[Year] = Year(@ToDate)
         AND T.[Month] <= Month(@ToDate)
      )
   );

Je však pochopitelné, že takovou konstrukci nechcete použít, protože je velmi nepohodlná. Zde je tedy kompromisní dotaz, který alespoň používá numerické výpočty a bude využívat méně CPU než výpočet konverze z data na řetězec (i když ne o dost méně na to, aby nahradil nucené skenování, což je skutečný problém s výkonem).

SELECT *
FROM dbo.YourTable T
WHERE
   T.[Year] * 100 + T.[Month] BETWEEN 201111 AND 201202;

Pokud máte index na Year , můžete získat velkou podporu odesláním dotazu následujícím způsobem, který má možnost hledat:

SELECT *
FROM dbo.YourTable T
WHERE
   T.[Year] * 100 + T.[Month] BETWEEN 201111 AND 201202
   AND T.[Year] BETWEEN 2011 AND 2012; -- allows use of an index on [Year]

I když to porušuje váš požadavek na použití jediného BETWEEN výraz, není o moc bolestivější a bude fungovat velmi dobře s Year index.

Můžete také změnit svůj stůl. Upřímně řečeno, použití samostatných čísel pro části data namísto jednoho sloupce s datovým typem data není dobré. Důvod, proč to není dobré, je přesně ten problém, kterému právě teď čelíte – je velmi těžké se na něj zeptat.

V některých scénářích datových skladů, kde hodně záleží na úsporách bajtů, bych si dokázal představit situace, kdy byste mohli uložit datum jako číslo (například 201111 ), ale to se nedoporučuje. nejlepší řešením je změnit tabulku tak, aby používala data namísto rozdělení číselné hodnoty měsíce a roku. Jednoduše uložte první den v měsíci a uvědomte si, že platí pro celý měsíc.

Pokud změna způsobu použití těchto sloupců není možná, ale stále můžete změnit tabulku, můžete přidat trvalý vypočítaný sloupec:

ALTER Table dbo.YourTable
   ADD ActualDate AS (DateAdd(year, [Year] - 1900, DateAdd(month, [Month], '18991201')))
   PERSISTED;

S tímto můžete jednoduše:

SELECT *
FROM dbo.YourTable
WHERE
   ActualDate BETWEEN '20111101' AND '20120201';

PERSISTED Klíčové slovo znamená, že i když stále získáte skenování, nebude muset provádět žádné výpočty na každém řádku, protože výraz se vypočítává při každém INSERT nebo UPDATE a ukládá se do řádku. Ale můžete získejte vyhledávání, pokud do tohoto sloupce přidáte index, díky čemuž bude fungovat velmi dobře (i když celkově to stále není tak ideální jako změna na použití sloupce skutečného data, protože to zabere více místa a ovlivní INSERTy a AKTUALIZACE):

CREATE NONCLUSTERED INDEX IX_YourTable_ActualDate ON dbo.YourTable (ActualDate);

Shrnutí:pokud opravdu nemůžete změnit tabulku žádným způsobem, budete muset udělat nějaký kompromis. Nebude možné získat jednoduchou syntaxi, kterou chcete, která by také fungovala dobře, když jsou data uložena rozdělená do samostatných sloupců.



  1. Proč jsou v seznamu sloupců pro příkazy INSERT ignorovány předpony oddělené tečkami?

  2. Jak upravit passthrough / passthru dotaz?

  3. MySqlException na ExecuteReader výběrem UserID(PK)

  4. unpivot s dynamickými sloupci plus názvy sloupců