Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 9092297
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 16, 20262026-06-16T22:48:29+00:00 2026-06-16T22:48:29+00:00

I have a SQL query that runs on a SQL 2008 R2 server. It

  • 0

I have a SQL query that runs on a SQL 2008 R2 server. It is run on the 1st and the 16th of the month. The query contains the following logic for setting StartDate and EndDate parameters:

DECLARE     @RunDateTime datetime
DECLARE     @RunDate datetime
DECLARE     @StartDate datetime
DECLARE     @EndDate datetime
DECLARE     @Month int
DECLARE     @Year int
DECLARE     @strStartDate varchar(10)

SET     @RunDateTime = GetDate()
SET     @RunDate = CAST(@RunDateTime AS DATE)

IF                  DATEPART(d, @RunDate) = 16
    BEGIN       
                    SET @StartDate =  DATEADD(d, -15, @RunDate)
                    SET @EndDate = @RunDate
    END
ELSE
    BEGIN
                    IF      Month(@RunDate) = 1
                            SET @Month = 12
                    ELSE    
                            SET @Month = Month(@RunDate) - 1

                    IF      Month(@RunDate) = 1
                            SET @Year = Year(@RunDate) - 1
                    ELSE
                            SET @Year = Year(@RunDate)

                    SET @strStartDate = CAST(@Year AS VARCHAR) + CAST(@Month AS VARCHAR) + '16'
                    SET @StartDate = CONVERT(datetime, @strStartDate)
                    SET @EndDate = @RunDate
    END

If the query is run on the 16th, we want the date range to be from the 1st to the 15th of the month. If the query is run on the 1st, we want the date range to be from the 16th to the end of the previous month.

Due to changing requirements at work, we’ve been told we need to find a way to do this without using any conversion of dates to strings. Is this possible, and does anyone have any idea how it might be done? I am very quickly getting out of my depth here.

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-16T22:48:31+00:00Added an answer on June 16, 2026 at 10:48 pm

    This will meet your specification. It doesn’t care what day you run it on, and it adjusts the EndDate to be the current date if it is not on an exact boundary. I could not compare exactly to your given code because it has errors for some dates (such as 20120201).

    DECLARE
       @StartDate datetime,
       @EndDate datetime;
    
    SELECT
       @StartDate = Convert(date, M.Anchor + X.S),
       @EndDate = Convert(date,
          M.Anchor + Day(GetDate() - 1)
          - CASE WHEN Day(GetDate()) IN (1, 16) THEN 1 ELSE 0 END
       )
    FROM
       (VALUES (1, 15, 0), (16, 31, 15)) X (F, T, S)
       CROSS APPLY (
          SELECT DateAdd(month, DateDiff(month, 0, GetDate() - 1), 0)
       ) M (Anchor)
    WHERE Day(GetDate() - 1) BETWEEN X.F AND X.T;
    

    I tested this code against a year of dates and I believe everything is working correctly.

    Verify its correctness yourself by checking out the results of a year of calculations in this SqlFiddle.

    Basically: Calculate the first of the month from yesterday’s date. Using this date, determine the offset from the start of the month to compute the starting date (if the 1st – 15th, use 0; if the 16th – 31st, use 15). Finally, compute the end date from the start of the month + the calculated day, making a special adjustment for the 1st and 16th to use the prior day’s end period.

    There’s actually no harm in always specifying through to the end of the period (1 – 16 or 1 – 28/29/30/31) rather than through the day run on, but that’s not what you asked for.

    To answer your questions about what is going on in the query.

    1. A derived table is a rowset-returing query, normally starting with a SELECT, wrapped in parentheses and given an alias. For example:

      SELECT X.FullName
      FROM
         (
            SELECT FullName = FirstName + ' ' + LastName
            FROM dbo.User
            WHERE ID = 123
         ) AS X
      

      Here we have derived table X consisting of the result of the query inside it. I like to skip the AS part because to me it just adds clutter. Note this was just a simple example–derived tables are generally more complex than this and are useful in a variety of situations. I used no alias on the dbo.User table but it is best practice to do so. But it is important to note that all column references in a derived table are resolved from within the derived table. No using tables from outside.

    2. VALUES is a way to create a rowset from values provided in a query. The normal syntax you might be familiar with is INSERT dbo.Table VALUES (1, 'a');. In SQL Server 2008 this was extended to allow multiple rows at the same time, as in INSERT dbo.Table VALUES (1, 'a'), (2, 'b');. I additionally allows this special multi-row notation in place of a SELECT query inside of a derived table. For example:

      SELECT X.*
      FROM (VALUES (1, 'a'), (2, 'b')) X
      -- Alias X is for a derived table of 2 rows & 2 unnamed columns
      
    3. A derived table’s column names are normally discovered from the query. See that in #1, X.FullName actually refers to an expression containing multiple columns from table dbo.User. The expression was explicitly given a new alias in the query. If you did not provide an alias, you would get an error, because the expression has no intrinsic name. In fact, the query from the prior point will not run as-is because derived table X has no column names! However, there is a syntax for providing explicit column aliases outside of a derived table:

      SELECT X.ID, X.Name
      FROM (VALUES (1, 'a'), (2, 'b')) X (ID, Name)
      

      I prefer this second syntax because it really helps make clear what is intended, it helpfully separates the expressions from their names, and generally to me is all around better for column-aliasing in derived tables. Many times I am copying and pasting what is inside the derived table from elsewhere, and it is nice to not have to cobble in column names each time. The aliases F, T, and S are for me From, To, and Start. I could have made them the longer names, but chose not to.

    4. Using SELECT Alias = <Expression> is just a shortcut for SELECT <Expression> AS Alias. I prefer the = method because then the aliases are reliably on the left side in one easy scannable column instead of at the ends of variable-length expressions. This syntax also has benefits due to being able to change to variable names with the addition of one character, or convert the query to an UPDATE statement easily.

    5. You can change any query to assign values to variables instead of returning a rowset just by adding @Variable = in front of each column expression. One gotcha to be aware of is that if a query returns multiple rows, while you can still use the @Variable = syntax, the server will still do all the work of materializing all the rows, and your variable(s) will simply have the value(s) from some, one, row. It may be the first row, or the last one, but even if you think there is a consistent row returned you should always assume it will be a random row. If you need a particular row then provide a WHERE clause or a TOP statement with an ORDER BY to force a particular row’s values to be used.

    6. CROSS APPLY is simply a derived table that has the special property of being allowed to have an “outer reference”, that is, it can use column values from tables introduced before it, within the parentheses. It restricts rows when no rows are returned (just like an INNER JOINed derived table), or OUTER APPLY does not restrict rows (just like an OUTER JOINed derived table). It requires no ON clause because all the filtering is done via a WHERE clause. The optimizer is quite good about understanding the intent of your query and doesn’t end up running the APPLY once for every outer row–it almost always is able to get the data intelligently as if it were just a regular join. I used it here as a cheap way to get a calculated value in my query that I could use more than once. I could as well have done DECLARE @MonthDate = <same expression> and used that instead, but some part of me likes not declaring variables when I don’t have to (as the @MonthDate variable is only needed for a single query).

    P.S. I would like to point out one thing I see in your example code (if you will allow me). Consider this section:

    IF   Month(@RunDate) = 1
         SET @Month = 12
    ELSE    
         SET @Month = Month(@RunDate) - 1
    
    IF   Month(@RunDate) = 1
         SET @Year = Year(@RunDate) - 1
    ELSE
         SET @Year = Year(@RunDate)
    

    All this lining up is great for trying to convey the intent of the IF / ELSE blocks, but in my professional opinion, you should be using BEGIN and END. One reason that the person who coded this chose to lay out the conditions this way is that the indenting style is (to me) a little excessive. Each BEGIN ends up being 2 indents deep, one for the IF (treating it as one indent block) and one for the BEGIN .. END. But this attachment to single-statement IFs without blocks leads to several problems.

    1. The above expression does not convey intent properly and forces a reviewer to evaluate why something is being done twice and that in fact the two conditions are the same. For starters, the block should be rewritten as so:

      IF   Month(@RunDate) = 1
           BEGIN
              SET @Month = 12;
              SET @Year = Year(@RunDate) - 1;
           END
      ELSE
           BEGIN
              SET @Month = Month(@RunDate) - 1;
              SET @Year = Year(@RunDate);
           END;
      

      This can now be sensibly understood and reviewed.

    2. As soon as you start to add multiple conditions or nested levels, you can get in deep trouble and find it almost impossible to debug. What if some enterprising developer, recognizing that the two conditions were identical and it made the most sense to combine their contents, wrote this code:

      IF   Month(@RunDate) = 1
           SET @Month = 12;
           SET @Year = Year(@RunDate) - 1;
      ELSE
           SET @Month = Month(@RunDate) - 1;
           SET @Year = Year(@RunDate);
      

      This looks good, but hides the fact that the second SET statement does not belong to the IF before it. Now, it won’t compile properly because the ELSE is orphaned. But what if the change were to this:

      IF   Month(@RunDate) = 1
           SET @Month = 12;
           IF @SpecialFlag = 1
              SET @Year = Year(@RunDate) - 1;
      ELSE
           SET @Month = Month(@RunDate) - 1;
           IF @SpecialFlag = 1
              SET @Year = Year(@RunDate);
      

      Now we have an awful mess! This will parse correctly, but be far from the result the developer intended: the ELSE block is part of the @SpecialFlag condition! It sure doesn’t look like it in the code, due to the indentation.

    So while I understand that code formatting conventions can be preferred with strongly-held convictions, and organizations and people can be greatly resistant to changes, I would like to suggest that you will achieve some benefits if 1) you use BEGIN and END in all IF blocks, and 2) in order to alleviate the struggle this causes due to the hassle of double-indenting everything, that you reformulate your block-indentation practice like so:

    • Instead of IF and ELSE beginning a block that has no end, and BEGIN being matched with END at a deeper level;

    • Make IF and ELSE begin blocks that are ended with END, and put BEGIN at the end of the line.

    This would then be:

    IF Month(@RunDate) = 1 BEGIN
       SET @Month = 12;
       SET @Year = Year(@RunDate) - 1;
    END
    ELSE BEGIN
       SET @Month = Month(@RunDate) - 1;
       SET @Year = Year(@RunDate);
    END;
    

    I recognize this looks very strange to someone used to a different style of indenting–all change is hard at first. But I believe that with a little practice it will grow on you and become less painful. It is just a matter of training the eye to match END with something besides BEGIN. Eventually you realize that you don’t care to even look for BEGIN because it adds no additional meaning to the lines beginning with IF or ELSE. Finally, you don’t have to do crazy, labor-intensive indenting patterns (including putting multiple spaces after IF and ELSE to get everything to line up) because the indenting just works, and you’re only doing a single level for each block instead of many. And you’ll never have code that falls out of the IF block unexpectedly as shown above.

    To make it 100% clear: with this style, if a BEGIN/END block has only one statement in it, nothing has to be rearranged in order to add a second, and the code will not break.

    Finally, please note that I have added semicolons in my own examples, for the simple reason that in SQL Server they will one day be required and I’d like all my production code to keep working without needing a giant and painful semicoloning project! This also gives the benefit of explicitly indicating when your block has stopped (though frankly, I don’t know if a semicolon is required after the END preceding an immediate ELSE–if so, I’ll at least have a small semicoloning project instead of a giant one).

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I have VBA code to run a query in SQL-Server 2008. It runs fine
I have created a SQL query that runs on SQL Server 2008. For some
i have the following SQL Query which runs on SQL Server CE 4 SELECT
We have an interesting table query (SQL Server 2008) that fails with a different
I have a simple query that runs in SQL 2008 and uses a custom
I have the following SQL query that runs fairly well, however it seems to
I have a query that runs against a sql server database that produces a
I have a query in SQL Server 2008. SELECT CONVERT(char(80), i.InvDate,3) AS InvDate, i.InvoiceNo,
I have a SQL query that I used my vb.net application to run it,
I have a Report Server that runs SSRS 2008 R2 Standard. This server retrieves

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.