简体   繁体   中英

Assigning values for [Month] parameter in a stored procedure

I have a stored procedure, for which the output is a table with [Year] and [Month] columns along with many other columns. I have also created four parameters as @SYear, @SMonth, @EYear, @EMonth. The distinct of [year] is 2015 and 2016. In the month column I have got few months like 2,3,5,7,8 etc in the year 2015 and 1,2 in the year 2016. Now I want to assign the values from the year and month columns to the parameters. In case of year I was able to assign it as ([Year] Between @SYear and @EYear). But when it comes to month if I assign ([Month] BETWEEN @SMonth AND @EMonth), the problem is practically the 5th month of 2015 is less than 1st month of 2016, but as sql is taking [Month] as integer 5 is greater than 1., so it isn't working. If i select @SYear = 2015, @SMonth = 5, @EYear = 2015, @EMonth = 7 it works.

Please suggest a possible way to assign the month column to the starta nd end month parameters.

BTW @S is start @E is end.

Hope I am clear enough for you guys to understand the problem, please let me know if anyone doesn't understand.

thank you.

Edit: 在此处输入图片说明

Here when I select the start year, start month and end year, end month as parameters I should get the results from the table.

Not an answer, long comment

This is ugly, I Know, if you can't work with DATEFROMPARTS() something like this might work (or cast strings)

SELECT *

FROM table1 t

WHERE
DATEADD(mm, t.Month -1, dateadd(yy,t.Year-1900,'19000101'))
BETWEEN
DATEADD(mm, @sMonth -1, dateadd(yy,@sYear-1900,'19000101'))
AND
DATEADD(mm, @eMonth -1, dateadd(yy,@eYear-1900,'19000101'))

This replaces my previous answers so that it answers the question fully. This turned out to be one of those things that is a lot harder than it seems it should be. I really thought Answer #2 below (creating a calculated field) would be the sure-thing fallback answer, but turns out you cannot use CAST() with a DATE type in a PERSISTED field, so that added to the complexity.

THIS IS ANSWER 1, use it if...

  • You need to do this query frequently
  • You have lots of data and need an index
  • You don't mind changing your table structure

The 'right way' is to add a column that is an actual DATE by adding a field with the actual MONTH/YEAR and a dummy "1st" for the day. Store it as a DATE and then use it for querying. This can be indexed and will be as fast as it gets.

THIS IS ANSWER 2, use it if...

  • You need to do this query frequently
  • You have minimal data and therefore do not need an index
  • You don't want to change your table structure

You can create a calculated field on your table using either of the following methods. This way whenever you write a query you can just use the date field and life is simple -- except you will not hit on an index.

ALTER TABLE table1
ADD THE_DATE1 AS CONVERT(VARCHAR(4), THE_YEAR ) + '-' + CONVERT(VARCHAR(2), THE_MONTH) + '-01'

ALTER TABLE table1
ADD THE_DATE2 AS DATEADD(mm, THE_MONTH -1, dateadd(yy,THE_YEAR-1900,'19000101'))

ALTER TABLE table1 -- SQL212+
ADD THE_DATE3 AS DATEFROMPARTS(THE_YEAR, THE_MONTH,  1)

THIS IS ANSWER 3, use it if...

  • You need to do this query frequently
  • You have lots of data and do need an index
  • You don't mind crazy queries

You can use a crazy WHERE statement to get your data. There are two version below, the second version includes some AND statements that will help SQL SERVER hit on some indexes (because SQL doesn't like having a bunch of OR operators in your WHERE statement).

DECLARE @table1 AS TABLE (
         ID INT ,
         THE_YEAR INT ,
         THE_MONTH INT ,
         THE_MONEY INT
        )

INSERT  INTO @table1
VALUES  ( 1, 2015, 5, 20 ),
        ( 2, 2015, 7, 50 ),
        ( 3, 2015, 9, 8 ),
        ( 4, 2015, 10, 60 ),
        ( 5, 2015, 12, 30 ),
        ( 6, 2016, 1, 90 ),
        ( 7, 2016, 2, 120 ),
        ( 8, 2017, 1, 220 ), -- Added for testing
        ( 9, 2017, 2, 320 )  -- Added for testing

DECLARE @START_YEAR INT = 2015;
DECLARE @START_MONTH INT = 7;

DECLARE @STOP_YEAR INT = 2015;
DECLARE @STOP_MONTH INT = 10;

-- QUERY THAT ANSWERS YOUR QUESTION

SELECT  *
FROM    @table1
WHERE   (
            ( @START_YEAR <> @STOP_YEAR AND THE_YEAR  = @START_YEAR AND THE_MONTH >= @START_MONTH )
            OR 
            ( @START_YEAR <> @STOP_YEAR AND THE_YEAR > @START_YEAR AND THE_YEAR < @STOP_YEAR )
            OR 
            ( @START_YEAR <> @STOP_YEAR AND THE_YEAR  = @STOP_YEAR  AND THE_MONTH <= @STOP_MONTH )
            OR 
            -- Handle when Start/Stop year are the same
            ( @START_YEAR  = @STOP_YEAR  AND THE_MONTH >= @START_MONTH AND THE_MONTH <= @STOP_MONTH )
        )
ORDER BY THE_YEAR ,
        THE_MONTH


-- QUERY THAT ANSWERS YOUR QUESTION, with INDEX optimizations
-- because once you have a bunch of data, this may help you hit on an index.
SELECT  *
FROM    @table1
WHERE   THE_YEAR >= @START_YEAR   -- Will hit an INDEX if you have one on THE_YEAR
        AND THE_YEAR <= @STOP_YEAR -- Will hit an INDEX if you have one on THE_YEAR
        AND
        (
                ( @START_YEAR <> @STOP_YEAR AND THE_YEAR  = @START_YEAR AND THE_MONTH >= @START_MONTH )
                OR 
                ( @START_YEAR <> @STOP_YEAR AND THE_YEAR > @START_YEAR AND THE_YEAR < @STOP_YEAR )
                OR 
                ( @START_YEAR <> @STOP_YEAR AND THE_YEAR  = @STOP_YEAR  AND THE_MONTH <= @STOP_MONTH )
                OR 
                -- Handle when Start/Stop year are the same
                ( @START_YEAR  = @STOP_YEAR  AND THE_MONTH >= @START_MONTH AND THE_MONTH <= @STOP_MONTH )
            )
ORDER BY THE_YEAR ,
        THE_MONTH

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM