Month to Date in SQL Server 2008 - sql

Hopefully this will be an easy one to answer.
I am working on a table that requires MTD data. One of our SQL guys told me to use
MONTH (#monthtodate)= 11
Where #monthtodate is set to GetDate() in the parameter list in SQL Server Management Studio. So in "theory", he says, it should select the month (11) and then get today and return all the requested data in between those two dates. But I'm thinking this isn't correct.
In looking at my data I'm starting to think that It's just returning data for the whole month of November instead of just MTD. I guess, technically, anything that has 0 won't be calculated. However that just means it's poorly written code correct?
In your opinions, would this be the better way to return MTD data:
production_date <= #today and Production_Date >= DATEADD(mm, DATEDIFF(mm, 0, #today), 0)
Thanks in advance everyone!

Here's how I do it. This should work on pretty much any version of SQL Server.
One important thing to note: at the outset, one should always establish a single value that represents 'now', the current moment in time. If you do not have a consistent value for now in your query, you will eventually get bit when your query is executed such that it crosses a date boundary whilst in-flight. Nothing like billing somebody for something they already paid for last month. Worst, edge-case bugs like that are difficult to catch, either by developers or by QA, since neither is likely to be working, say, at 11:59 on December 31.
The code:
declare
#dtNow datetime ,
#Today datetime ,
#dtFrom datetime ,
#dtThru datetime
---------------------------------------------------------------------------------------
-- set our effective notion of 'now'-ness.
--
-- We need have a consistent notion of now, lest we get bit in the a$$
-- by an edge case where we cross a day/month/year boundary in mid-execution.
--
-- NOTE: Mostly, we're interested in the *DATE* rather than the actual moment-in-time.
-- So, we carry around two flavors here.
---------------------------------------------------------------------------------------
set #dtNow = current_timestamp
set #Today = convert(datetime,convert(varchar,#dtNow,112),112)
---------------------------------------------------------------------------------------
-- compute the current date.
--
-- 1. get the current date sans timestamp (effectively start-of-day)
-- 2. add 1 day, then back off 3 millseconds to set it to the last tick of the current day
--
-- NOTE: Depending on the requirements of your particular application (and the nature
-- of your data), you might want to use the actual current date/time value as
-- your upper bound.
--
-- FURTHER NOTE: How far to back off is dependent on your date/time type:
--
-- * For DateTime, the resolution is milliseconds and the last tick of the day
-- is 997 milliseconds, so you need to back off 3ms from the start of the
-- next day.
--
-- * SmallDateTime has a 1 second resolution. The last tick of the day, natch,
-- is 59 seconds, so you need to back off 1 second from the start of the next day.
--
-- * For DateTime2, the user declares the precision in decimal fractions of a second,
-- though its resolution is 100ns ticks. You'll need (especially if you're working
-- with DateTime2 columns/variables of differing precision) experiment to figure out
-- what traps Microsoft has set for you inside DateTime2 and what you need to do to
-- make things work properly.
--
---------------------------------------------------------------------------------------
set #dtThru = dateadd(ms,-3,dateadd(day,1,#Today))
--set #dtThru = #dtNow -- if you need the actual current date/time value
---------------------------------------------------------------------------------------
-- compute start of month
--
-- We do this by subtracting the day number of 'today' from the date/time value #today.
-- That gives us the last day of the prior month. Then we add one day to get the first
-- day of the current month.
---------------------------------------------------------------------------------------
set #dtFrom = dateadd(day,1-day(#Today),#Today)
---------------------------------------------------------------------------------------
-- finally, make your query for 'current month to date'
---------------------------------------------------------------------------------------
select *
from dbo.foobar t
where t.recorded_date between #dtFrom and #dtThru

If you are asking which of these 2 queries is better from a performance standpoint:
DECLARE #now datetime = GETDATE()
SELECT *
FROM yourTable
WHERE Production_Date >= DATEADD(mm, DATEDIFF(mm, 0, #now), 0)
AND Production_Date < #now
SELECT *
FROM yourTable
WHERE YEAR(Production_Date) = YEAR(#now)
AND MONTH(Production_Date) = MONTH(#now)
AND Production_Date < #now
Then the first one would be, since it will use the index on Production_Date if there is one. However, they should both return the same results.

Related

SQL Code to Determine IF Test Date of (A) is Within N Days of Test Date (B) then Return Members Row with Both Dates

I'm writing a SQL query in Teradata to determine which members had an eGFR and uACR test done within 4 days of each other. All of the tests are being pulled in correctly, but not sure who to go about this in the WHERE clause.
SQL Code Sample
I only need data returned where this is true.
I tried this
AND [uACR_2b_2_DATE] <= ([uACR_2b_1_DATE] + 4 Days)
also
other attempt
sample code2
In sql server there is DATEDIFF to determine the distance, this works with days, hours, etc.
DECLARE
#dt1 DATETIME = '20230102 23:59:59'
,#dt2 DATETIME = '20230103 00:00:00'
SELECT difference_ABS = ABS( DATEDIFF(DAY, #dt1, #dt2) )
This ignores the time part, and gives the absolute (no negatives) of the distance of the date part. So in this example You get one day distance, although the two timestamps are one minute apart. If this is what You would like to use, just put it in a WHERE-CLAUSE like so
ABS( DATEDIFF(DAY, uACR_2b_1_DATE, uACR_2b_2_DATE) ) <= 4

Improper calculation of a date difference in sql

I need to calculate the difference between DFU.HISTSTART and the last Sunday which is for today is 2/27. It should be dynamic and change every Sunday.
For some reason for this calculation I am getting 3 and should get 4.
,ABS(DATEDIFF(wk,
DATEADD(wk,
DATEDIFF(wk,6,GETDATE()), 0), DFU.HISTSTART))
AS '#WKS of Hist'
Does someone have any ideas?
You have two problems... the first is that you're trying to do the old offset trick with the "6". That works on other date parts but not on week. From the Microsoft Documentation...
For a week (wk, ww) or weekday (dw) datepart, the DATEPART return
value depends on the value set by SET DATEFIRST.
If your DATEFIRST is set to 7 (you can verify by running SELECT ##DATEFIRST;) AND your weeks start on Sundays, the following will work just fine and return a "4".
--===== Setup just the dates in question for a demo
DECLARE #HistStart DATE = '01-30-22'
,#Today DATE = '02-27-22'
;
--===== Demo the "right" way to use "wk".
-- I say "right" way because I don't trust DATEFIRST.
SELECT DATEDIFF(wk,#HistStart,#Today)
;
GO
The second thing is that it's generally a really bad practice to depend on the DATEFIRST setting in this global computing environment. Instead, do the much more universal/bullet-proof method of using Integer math to calculate the number of weeks it's been since date-serial 6, which you correctly identified as a Sunday.
--===== Setup just the dates in question for a demo
DECLARE #HistStart DATE = '01-30-22'
,#Today DATE = '02-27-22'
;
--===== Demo "Bulletproof" Way to calculate the difference in Weeks starting on Sunday
SELECT DATEDIFF(dd,6,#Today)/7 - DATEDIFF(dd,6,#HistStart)/7
;
If you need to calculate week differences in weeks a lot, you might want to turn that into a function so that if the company decides to change the day of the week that is the start of the week, you'll only need to change it in one place. In fact, you might want to have the function read it (the date-serial for the starting day of the week) from a "general settings table".
Another way of doing this:
with last_sunday as (
SELECT
case DAYNAME(current_date())
when 'Sun' then current_date()
when 'Mon'then current_date()-1
when 'Tue'then current_date()-2
when 'Wed'then current_date()-3
when 'Thu'then current_date()-4
when 'Fri'then current_date()-5
when 'Sat'then current_date()-6
else '2020-01-01' end "SUNDAY_DATE"
)
SELECT
DFU.HIST_START_DATE
,LAST_SUNDAY.SUNDAY_DATE
,datediff(week,DFU.HIST_START_DATE,LAST_SUNDAY.SUNDAY_DATE) weeks_diff
FROM DFU
JOIN LAST_SUNDAY
;

How to write a variable within SQL using Dateadd and DateDiff for finding the last TWO days of the previous month

I am trying to write a variable using the dateadd and datediff that shows the last two days of previous month. One variable will be the second to last day of the previous month, the one I am having trouble with. The other will be the last day of the previous month, the one I was able to get. I am using SQL Server.
I've tried looking for it on Stack and I have only seen the last day of the previous month given and NOT the second to last day. I tried learning the dateadd and datediff, (which I still want to do).
This is what I tried so far:
Declare #CurrentMonth as date = '3/1/2019'
Declare #SecLastDayPrevMonth as date = DATEADD(MONTH, DATEDIFF(MONTH, 0, #currentmonth), -2)
Declare #LastDayPrevMonth as date = DATEADD(MONTH, DATEDIFF(MONTH, 0, #currentmonth), -1)
The results I am getting for the seclastdayPrevMonth is 2/28/2019. Instead I would want 2/27/2019
I am also getting 2/28/2019 for lastdayprevmonth which is what I want.
I am writing variables because the current month will change every month, and instead of having to update the other days I need within my query, I want to use variables so I am only updating the current month and everything else is flowing through.
And explanation as to why my dateadd/datediff is wrong and an explanation for why the correct dateadd/datediff is the way it is, will be very helpful
Why not refer to the last day when calculating the second last day? Also, your usage of DATEADD is very weird. The syntax is DATEADD(interval, increment, datetime)
Declare #recmonth as date = '3/1/2019'
Declare #LastDayPrevMonth as date = EOMONTH(DATEADD(MONTH, -1, #RecMonth))
Declare #SecLastDayPrevMonth as date = DATEADD(DAY, -1, #LastDayPrevMonth)
SELECT #SecLastDayPrevMonth, #LastDayPrevMonth
So we can calculate the last day of the previous month by subtracting one month from a date and then calling EOMONTH, which returns the last day of a given month. Then the second last day is just subtracting one day from that.
Yields:
SecLastDayPrevMonth LastDayPrevMonth
------------------- ----------------
2019-02-27 2019-02-28
As to "why", DATEDIFF() takes 3 arguments: datepart (string representation of a specific date part), startdate, enddate (both of which must be convertible to a date-ish object).
0 is essentially SQL's epoch, which, in this case is 1/1/1900. So the difference in months between 0 and 3/1/2019 is (119*12)+2 (+2 because we exclude March, since we aren't calculating a full month) = 1430 months difference.
Then, we are trying to add 2 months to our value. DATEADD() takes 3 arguments: datepart, number, date. But, in the example, you are adding 1430 months to whatever date -2 gets converted to (in this case, I believe it would be 12/30/1899, or 2 days before epoch). So, 1430 months after 12/30/1899 would be 2/30/2019, but February only has 28 days in 2019, so it returns 2/28/2019. In a Leap Year, it probably would return 2/29/2019.
To get your #LastDayPrevMonth and #SecLastDayPrevMonth with only DATEDIFF() and DATEADD(), you just need to change your calculations a little.
First thing you want to do is find the First Day of your Given Month. That can be done with DATEADD(month,DATEDIFF(month,0,#CurrentDate),0). We're essentially using the same thing we used above to calculate the number of months since epoch, but then we are adding those months back to epoch.
Now that we know the First Day of the Given Month, all we have to do is subtract days to get a day from the prior month.
So,
DECLARE #CurrentDate date = '2019-03-15' ; -- Changed it to something in the middle of the month.
DECLARE #FirstDayOfGivenMonth = DATEADD(month,DATEDIFF(month,0,#CurrentDate),0) ; -- 3/1/2019
DECLARE #LastDayOfPrevMonth date = DATEADD(day,-1,#FirstDayOfThisMonth) ; -- 2/28/2019
DECLARE #SecLastDayOfPrevMonth date = DATEADD(day, -2, #FirstDayOfThisMonth) ; -- 2/27/2019
SELECT #LastDayOfPrevMonth AS LDPM, #SecLastDayOfPrevMonth AS SLDPM ;
DECLARE #FourDaysLeftInPrevMonth date = DATEADD(day, -4, #FirstDayOfThisMonth) ; -- 2/25/2019
SELECT #FourDaysLeftInPrevMonth AS FourDaysLeftPrev ;
Granted, since SQL 2012, this can all be accomplished much easier with the EOM() function to get to the last day of a month. But if you only could use the two original functions from your original question, this would be one way to get to your needed values.

Calculating how many Working Days between 2 Dates - T-SQL?

I realise different solutions will have different variations of what "Working Days" means but in my case I mean Monday to Friday inclusive.
Basically I have Created a function to do the calculation for me and my current solution works. My concern (and reason for asking this question) is that I am worried that this is a bad way of achieving this because the function is being called with a very high frequency. In the last 3 months it has been called 12 million times on a production system, with the average worker time 44ms.
This lead me to wonder if this is the correct way of achieving solution.
Firstly here is the function I created:
CREATE FUNCTION [dbo].[fn_WorkDays]
(
#StartDate DATETIME,
#EndDate DATETIME = NULL --#EndDate replaced by #StartDate when DEFAULTed
)
RETURNS INT
AS
BEGIN
--===== Declare local variables
--Temporarily holds #EndDate during date reversal
DECLARE #Swap DATETIME
--===== If the Start Date is null, return a NULL and exit
IF #StartDate IS NULL
RETURN NULL
--===== If the End Date is null, populate with Start Date value
-- so will have two dates (required by DATEDIFF below)
IF #EndDate IS NULL
SELECT #EndDate = #StartDate
--===== Strip the time element from both dates (just to be safe) by converting
-- to whole days and back to a date. Usually faster than CONVERT.
-- 0 is a date (01/01/1900 00:00:00.000)
SELECT #StartDate = DATEADD(dd,DATEDIFF(dd,0,#StartDate),0),
#EndDate = DATEADD(dd,DATEDIFF(dd,0,#EndDate) ,0)
--===== If the inputs are in the wrong order, reverse them
IF #StartDate > #EndDate
SELECT #Swap = #EndDate,
#EndDate = #StartDate,
#StartDate = #Swap
--===== Calculate and return the number of workdays using the
-- input parameters. This is the meat of the function.
-- This is really just one formula with a couple of parts
-- that are listed on separate lines for documentation
-- purposes.
RETURN (
SELECT
--Start with total number of days including weekends
(DATEDIFF(dd,#StartDate,#EndDate)+1)
--Subtact 2 days for each full weekend
-(DATEDIFF(wk,#StartDate,#EndDate)*2)
--If StartDate is a Sunday, Subtract 1
-(CASE WHEN DATENAME(dw,#StartDate) = 'Sunday'
THEN 1
ELSE 0
END)
--If EndDate is a Saturday, Subtract 1
-(CASE WHEN DATENAME(dw,#EndDate) = 'Saturday'
THEN 1
ELSE 0
END)
)
END
As a simple example of its use I would run this type of query:
SELECT MYTABLE.EntryDate
,dbo.fn_WorkDays(MYTABLE.EntryDate, getutcdate()) as WorkingDays
FROM MYTABLE
MyTable could contain 5000 rows all with different Dates in the EntryDate Column (5000 calls to Function)
My question is I am missing something here in the way that I am doing this, would it be beneficial to create a lookup table for this (but that is a lot of combinations of dates)
Any thoughts, improvements or recommendations would be appreciated...
I don't think there's a lot you can do with the UDF tbh - having it calculated at run-time like this in SQL is always going to incur a hit to some degree.
So, ideally (and this may not be possible as I don't know the full picture), I think what I'd do is store the WorkingDays number in your table and calculate it ONCE when the record is created. If that's not possible (i.e. when the record is created, you don't have an "end date" so it has to be worked out using "now") then I'd be considering a nightly scheduled job to go and recalculate all those particular records so that they are updated each day - then when an "end date" does get entered, that record does not get included in this batch update.
The benefits of this, are you offload the calculations to a quieter period, and only do the calculations once per day. The query becomes a lot simpler and more performant as it can just read the WorkingDays number from the table.
If that's not an option, then I'd suggest doing the calculations in the front end, remove the hit from the DB.
There's two problems here:
Calculating the number of days between two dates,
Identifying whether or not a give Date is a "business day".
The second includes easy ones like "weekday" versus "weekend", holidays (secular, religious, and legal), etc.
You'll need to solve both.
The first is easier, because relational databases will have functions to help you. It's the second that's harder and more variable, because it changes by locale and business.

Why does SQL Server 2005 miss a within range DATETIME value

Can anybody tell me why my Database selection queries asking for an orderid
WHERE [order].dateplaced >= DATEADD(millisecond,-3,ISNULL(#datefrom, #searchscope))
AND [order].dateplaced < DATEADD(millisecond,-3,DATEADD(day,1,ISNULL(#dateto, GETDATE())))
Is missing out an order over three and a half minutes before midnight on the #dateto when #datefrom and #dateto are both set to the same date?
I've tried altering the query so the initial DATEADD adds a second to both datetimes instead of taking away 3 milliseconds from both and it continues to not pull the order.
For reference the exact datetime of dateplaced is: 2009-01-20 23:56:17.933
Am I being dumb?
EDIT: I remember now why the three milliseconds thing came into play. It was because our accounts work on whole months and if you wanted a month's worth of reports you could set it from, for example, 01-Jan to 01-Feb and that would include everything up to midnight on 01 Feb. (Incidentally is that exclusive or inclusive?) However people were too dumb to actually set the date range to this they would set it from 01-Jan to 31-Jan and miss a day (don't ask me).
As I knew that SQL Server worked in resolutions of 3 milliseconds I first of all made a request for 31-Jan go until 11:59:59.997 on 31-Jan whereas 01-Feb would still go from midnight. However to compensate then on the date "from" I had to drop three milliseconds so nothing could slip through the cracks. I just presumed that SQL Server would be able to handle that. It's probably missing bits out of those reports now so I shall have to go and look those up.
Although the top-voted solution below works for all practical purposes (our bank's credit card settling software still randomly puts transactions from either side of midnight on the "wrong" side as far as our system is concerned) it still doesn't answer the question of why a transaction with a good three and a half minutes grace fails to get captured. I appreciate that just losing the time will work most of the time but the nature of our business means that on certain dates we have actual time periods of around twenty minutes where greater resolution and precision handling would be handy.
For the curious we sell concert tickets in the UK and on days where we have, for example, the Reading or V Festival going on sale we shift a couple of thousand tickets in the twenty minutes after on sale and the rest of the day have a normal amount of sales for other stuff. Those twenty minute periods become the target of much reporting and dissection as the load balancer isn't always perfect and weird record glitches do crop up. So being able to dice records down to stretches of seconds would be handy. My confidence in the software is a bit shaken by this so an actual answer would be handy.
However for the time being, the particular thing I'm doing is fine with the top-voted solution below...
GETDATE() has a time portion which you are not trimming off (you might want to trim the time part off of the other variables too). I don't know why you're messing around with milliseconds either. I know that 3 milliseconds is the smallest resolution, but I typically have never had to use it to work around range endpoints.
DECLARE #today AS DATETIME
SET #today = DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE()))
SET #datefrom = DATEADD(dd, 0, DATEDIFF(dd, 0, #datefrom))
SET #searchscope = DATEADD(dd, 0, DATEDIFF(dd, 0, #searchscope))
SET #dateto = DATEADD(dd, 0, DATEDIFF(dd, 0, #dateto))
SELECT *
FROM [order]
WHERE [order].dateplaced >= ISNULL(#datefrom, #searchscope)
AND [order].dateplaced < DATEADD(day, 1, ISNULL(#dateto, #today))
This works for me in SQL 2000 and 2008:
declare #datefrom datetime
declare #dateto datetime
set #datefrom='2009-01-20'
set #dateto='2009-01-20'
select case when '2009-01-20 23:56:17.933' >= DATEADD(millisecond,-3,#datefrom) then '>=' else 'NOT >=' end
select case when '2009-01-20 23:56:17.933' < DATEADD(millisecond,-3,DATEADD(day,1,#dateto)) then '<' else 'NOT <' end
There might be more to the problem than you're telling us.