SQL Server BETWEEN

I have a table with Year, Month and several numeric columns

Year Month Total 2011 10 100 2011 11 150 2011 12 100 2012 01 50 2012 02 200 

Now I want SELECT rows between FEB from November to November 2012. Note that I want Query to use a range. As if I had a date column in a table.

+4
source share
3 answers
 (Year > @FromYear OR Year = @FromYear AND Month >= @FromMonth) AND (Year < @ToYear OR Year = @ToYear AND Month <= @ToMonth) 
+2
source

By acquiring a way to use BETWEEN with a table, since it will work, but performance will be worse in each case:

  • It at best consumes more CPU in order to do some calculations on strings, and not work with them as dates.
  • With the worst power, the table scans every row in the table, but if your columns have indexes, then you can search with the right query. This could be a HUGE performance difference, since enforcing constraints on BETWEEN clauses will be disabled using the index.

I suggest the following if you have a pointer to date columns and do not care about performance at all:

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

However, it is clear that you do not want to use such a construction, since it is very inconvenient. So, here is a trade-off request that at least uses numerical calculations and will use less CPU than calculating a date to string conversion (although this is not enough to compensate for forced scanning, which is a real performance issue).

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

If you have an index on Year , you can get a big boost by sending a request as follows, which has the ability to search:

 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] 

While this violates your requirement to use a single BETWEEN expression, it is not too painful and will work very well with the Year index.

You can also change the table. Honestly, using separate numbers for your date details instead of a single column with a date data type is not good. The reason is that this is not good, because of the exact problem you are facing right now, it is very difficult to request.

In some data storage scenarios where many save bytes are important, I could imagine situations where you can save the date as a number (e.g. 201111 ), but this is not recommended. The best solution is to change the table to use dates instead of separating the numeric values โ€‹โ€‹of the month and year. Just save the first day of the month by finding out that it costs for the whole month.

If changing the way you use these columns is not an option, but you can still change your table, then you can add a constant computed column:

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

With this, you can simply do:

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

The keyword PERSISTED means that as long as you get the scan anyway, it does not need to do any calculations on each line, because the expression is evaluated for each INSERT or UPDATE and stored in the line. But you can get a request if you add an index to this column, which will make it work very well (although in general it is still not as ideal as switching to using the actual date column, as this will require more space and affect INSERT and UPDATE):

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

Summary: if you really cannot change the table in any way, then you will have to compromise somehow. It is not possible to get the simple syntax you want, which will also work well when your dates are stored as separate columns.

+7
source

Your sample table indicates that there is only one record per year and month (if it is indeed a monthly pivot table). If so, you are likely to get very little data in the table even after several decades of activity. A concatenated expression solution will work, and performance (in this case) will not be a problem:

 SELECT * FROM Table WHERE ((Year * 100) + Month) BETWEEN 201111 AND 201202 

If this is not the case, and you really have a large number of records in the table (more than several thousand records), you have several options:

  • Modify the table to store the year and month in YYYYMM format (either as an integer value or text). This column can replace the columns of the current year and index or be in addition to them (although this violates the normal form). Index this column and query it.

  • Create a separate table with one record per year and month, as well as an indexed column, as described above. In your query, attach this table to the source table and query the indexed column in the smaller table.

+1
source

All Articles