sql >> Databasteknik >  >> RDS >> Sqlserver

SQL Server MELLAN

Att komma på ett sätt att använda BETWEEN med bordet som det är kommer att fungera, men kommer att bli sämre prestanda i alla fall:

  • Det kommer i bästa fall att förbruka mer CPU att göra någon form av beräkning på raderna istället för att arbeta med dem som datum.
  • Det kommer i värsta fall att tvinga fram en tabellsökning på varje rad i tabellen, men om dina kolumner har index, är en sökning möjlig med rätt fråga. Detta kan vara en ENORM prestandaskillnad, eftersom att tvinga in begränsningarna i en BETWEEN-sats kommer att inaktivera användningen av indexet.

Jag föreslår följande istället om du har ett index på dina datumkolumner och bryr dig överhuvudtaget om prestanda:

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)
      )
   );

Det är dock förståeligt att du inte vill använda en sådan konstruktion eftersom det är väldigt besvärligt. Så här är en kompromissfråga, som åtminstone använder numerisk beräkning och kommer att använda mindre CPU än beräkning av datum-till-sträng-omvandling (men inte tillräckligt mindre för att kompensera för den påtvingade skanningen som är det verkliga prestandaproblemet).

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

Om du har ett index på Year , kan du få ett stort uppsving genom att skicka frågan enligt följande, som har möjlighet att söka:

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]

Även om detta bryter mot ditt krav på att använda en enda BETWEEN uttryck är det inte så mycket mer smärtsamt och kommer att fungera mycket bra med Year index.

Du kan också ändra ditt bord. Uppriktigt sagt är det inte bra att använda separata siffror för dina datumdelar istället för en enda kolumn med en datumdatatyp. Anledningen till att det inte är bra är på grund av det exakta problemet du står inför just nu – det är väldigt svårt att fråga.

I vissa datalagerscenarier där det är mycket viktigt att spara bytes kan jag föreställa mig situationer där du kan lagra datumet som ett nummer (som 201111 ) men det rekommenderas inte. Det bästa lösningen är att ändra din tabell för att använda datum istället för att dela upp det numeriska värdet för månaden och året. Spara helt enkelt den första dagen i månaden, och tänk på att den gäller för hela månaden.

Om det inte är ett alternativ att ändra hur du använder dessa kolumner, men du kan fortfarande ändra din tabell, kan du lägga till en beständig beräknad kolumn:

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

Med detta kan du bara göra:

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

PERSISTED nyckelord betyder att även om du fortfarande kommer att få en skanning, behöver den inte göra någon beräkning på varje rad eftersom uttrycket beräknas på varje INSERT eller UPDATE och lagras i raden. Men du kan få en sökning om du lägger till ett index på den här kolumnen, vilket kommer att få den att prestera mycket bra (även om detta allt som allt fortfarande inte är lika idealiskt som att ändra till att använda en faktisk datumkolumn, eftersom det tar mer utrymme och kommer att påverka INSERTs och UPPDATERINGAR):

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

Sammanfattning:om du verkligen inte kan ändra tabellen på något sätt, då måste du göra en kompromiss på något sätt. Det kommer inte att vara möjligt att få den enkla syntax du vill ha som också kommer att fungera bra, när dina datum lagras uppdelade i separata kolumner.



  1. Heroku Postgres DB långsammare efter uppgradering

  2. MySQL lagrade procedurvariabler från SELECT-satser

  3. SQL - Räkna antal ändringar i en ordnad lista

  4. Oracle:Konvertera en IPv4-adress till ett nummer?