How to get start and end of month in numeric type for SQL where clause - sql

I want to run a query monthly to look for records from the previous month. The where clause will be greater or equal to the 1st day of the month and less or equal to the last day. The difficultly I am having is the dates are stored as numbers in a column with a numeric data type. The format being used is yyyymmdd. We are currently manually changing the where clause, so e.g. show me any records with dates >=20201001 to <=20201031, but we need to automate this process. I have tried a few ways to try and solve this but need some guidance.
So far I've tried:
select concat( CONCAT(cast((Year(DATEADD(month, -1, getdate()))) as numeric),cast((Month(DATEADD(month, -1, getdate()))) as numeric)),'01')
Returns error message operand data type numeric is invalid for concat operator. Works in separate query window returning format yyyymmdd.
SELECT DATEADD(mm, DATEDIFF(mm, 0, (DATEADD(month, -1, getdate()))), 0)
Returns error message arithmetic overflow error converting expression to data type date time. Works partly in separate query window but returns yyyy-mm-dd hh:mi:ss:ms
Any help would be greatly appreciated.
Thanks

The real solution is fix the design; don't store dates in a datatype other than a date and time data type.
What you can do, however, is convert the value of GETDATE() to a numerical value of the same format:
SELECT ...
FROM ...
WHERE NumericalDate >= CONVERT(varchar(8),DATEADD(DAY, 1, EOMONTH(GETDATE(),-1)),112)
AND NumericalDate < CONVERT(varchar(8),DATEADD(DAY, 1, EOMONTH(GETDATE())),112)
For today, this would return rows where NumericalDate is in November 2020 (specifically on or after 20201101 and before but not on 20201201).
Note, I don't "bother" to CAST/CONVERT the varchar to a Numerical data type, as it'll be implicit cast due to data type precedence.

You can use artihmetics like so:
where dates >= year(getdate()) * 10000 + month(getdate()) * 100 + 1
and dates < year(dateadd(month, 1, getdate())) * 10000 + month(dateadd(month, 1, getdate())) * 100 + 1

Compare the month and forget the days.
BETWEEN 20201000 and 20201099 finds the same rows as your example >=20201001 to <=20201031
That means that you do not have to figure out how many days are in the month.

Related

SQL Server error in conversion of date from string

NPD.CreatedOn is defined as a datetime datatype column (in SQL Server).
SELECT *
FROM NPDMaster NPD
WHERE DATEDIFF(MONTH, CONVERT(VARCHAR(7), NPD.CreatedOn, 126), CONVERT(VARCHAR(30), GETDATE(), 126)) <= 6
I get this error:
Conversion failed when converting date and/or time from character string.
What can I try to resolve it?
Don't use things like DATEDIFF in the WHERE on your columns, such queries aren't SARGable and thus can (will) perform poorly. If you want rows where the date is on or after the start of the month 6 months ago then do the date logic on GETDATE()/SYSDATETIME()/etc:
SQL Server doesn't have a "start of month" function, but you can use EOMONTH and then add a day:
SELECT *
FROM dbo.NPDMaster NPD
WHERE NPD.CreatedOn >= DATEADD(DAY, 1, EOMONTH(GETDATE(),-7));
You don't need to convert the datetime values to text. DATEDIFF() expects datetime values as second and third argument:
SELECT *
FROM NPDMaster NPD
WHERE DATEDIFF(month, NPD.CreatedOn, GETDATE()) <= 6
The actual reason for the error (as is explained in the documentation), is that ...DATEDIFF implicitly casts string literals as a datetime2 type.

Query data from previous day when month/year changes

In a SQL Server query, I am currently using the clause
WHERE
DAY(trade_date) = DAY(GETDATE()) - 1
AND MONTH(trade_date) = MONTH(GETDATE())
AND YEAR(trade_date) = YEAR(GETDATE())
to query my data from the previous day.
It is working fine right now but my question is if, for example, on 8/1/2021, SQL Server will try to get data from 8/0/2021 or if it will know to get data from 7/31/2021.
If this query won't work what could I use instead? Thanks!
I would recommend using proper date comparison logic - instead of breaking it down to day, month and year. Also, it is recommended to use proper date arithmetic functions like DATEADD instead of just - 1 on your date values (never sure what that -1 stands for: minus one day? Week? Month? Hour?).
And lastly - I would also recommend using SYSDATETIME() instead of GETDATE() since the latter always returns a DATETIME datatype - which should be on its way out, and you should use DATE (if you don't need to time portion), or DATETIME2(n) (if you do need the time portion) since those are more efficient and have fewer limitations compared to DATETIME.
If your trade_date is a DATE column (as it probably should be), just use:
WHERE
trade_date = DATEADD(DAY, -1, SYSDATETIME())
and if it's not a DATE - just cast it to a date as needed:
WHERE
CAST(trade_date AS DATE) = DATEADD(DAY, -1, CAST(SYSDATETIME() AS DATE))

datediff(dd,... returning month data

I've come across this in a query:
[date_start] >= DATEADD(dd, -DAY(GETDATE()) + 1, DATEDIFF(dd, 0, GETDATE()))
I understand that this is in part removing the time element from the field, but can someone explain to me how this is returning the current month data without using the 'month' function?
It takes the current day of the month (day(GetDate())+1) and subtracts it from the number of whole days since day 0.
[Date_start] >=
DATEADD(dd, --Add Integer X to DateTime Y
-DAY(GETDATE()) + 1, --X = - Current Day of Month + 1
DATEDIFF(dd, 0, GETDATE()) --Y = The number of days between day 0 and now.
--(in whole days to round out the 'time' part of the datetime)
There's no standard function to achieve this.
Yours is not much less intuitive than other methods I've seen, although using a negative number in dateadd() is confusing, as is the reliance on the result of the datediff() being implicitly converted from int to datetime.
Other Methods Here: SQL Server Helper - Date Functions

Format varchar type and convert it to date variable

I have a varchar value in this format “YYYYMM-LL” where: YYYY is four digit year; MM is two digit month.LL is two digit length of reporting period. This is the number of whole months included in the calculations (from first to last day of each month).
My questions is how I could use this varchar value to decide two variables:#First_Date and #Last_Date.
Example: 201409-06
This scenario is for the six months ending in September of 2014.
The range of calendar dates in this scenario is 04/01/2014 through 09/30/2014.
So how could I use t-sql to find this two dates #First_Date : 04/01/2014 and #Last_Date: 09/30/2014.
Any help would be really appreciated!
You can do something like this:
select #firstdate = dateadd(month, 1 - cast(right('201409-06', 2) as int),
convert(date, left('201409-06', 6) + '01')
),
#lastdate = dateadd(day, -1,
dateadd(month, 1,
convert(date, left('201409-06', 6) + '01')
)
)
This parses the string to do the date arithmetic that you want. SQL Server recognizes a string in the form of "YYYYMMDD" as a date, regardless of internationalization settings.
And a SQL Fiddle.

How to only check the time on datetime fields but ignore the date?

I have a column that stores data in datetime format. I want to check for all instances where the time part of this column is not equal to 00:00:00:000 - the date does not matter.
Basically, if time() was a function, something like this:
SELECT *
FROM progen.DY
WHERE TIME(DY_DATE) <> '00:00:00:000'
How do I go about doing this?
You only need a minor tweak on what you already have.
SELECT *
FROM progen.DY
WHERE TIME(DY_DATE) <> '00:00:00:000'
Use CONVERT to change your DATETIME to a TIME.
SELECT *
FROM progen.DY
WHERE CONVERT(TIME, DY_DATE) <> '00:00:00:000'
Another way is to convert it to different datatype, eg
SELECT *
FROM progen.DY
WHERE CAST(DY_DATE as float) - CAST(DY_DATE as int) > 0
SQLFiddle Demo
I do this all the time when trying to see if a table's column should be turned into a date instead of a datetime, which is really the answer.
select *
from progen.dy
where cast(dy_date as Date) <> dy_date
the cast removes the time and datetime has higher precedence, so when compared, if the are unequal then it has a time value. Same thing could be done with a cast to time, with a bit of different syntax.
Use DATEDIFF and DATEADD to instead get the date part of the datetime. Compare the column against the date only, and it will return those rows that have a non-zero time.
The way this works is that we first calculate the difference (in days) between the epoch and the value. We add that number to the epoch to create a new datetime. Since the result of DATEDIFF is an integer, any time component gets rounded off.
SELECT *
FROM Table
WHERE DateColumn <> DATEADD(d, DATEDIFF(d, 0, DateColumn), 0)
The time function could then be implemented by the following, not that I recommend it for this specific scenario:
SELECT DATEDIFF(minute, DATEADD(d, DATEDIFF(d, 0, DateColumn), 0), DateColumn) as MinutesIntoDay,
-- or, if you require higher precision
DATEDIFF(second, DATEADD(d, DATEDIFF(d, 0, DateColumn), 0), DateColumn) as MinutesIntoDay
FROM Table
Edit: As mentioned in other answers, you can cast to DATE to achieve the same effect as DATEADD(d, DATEDIFF(d, 0, DateColumn), 0), which cleans up nicely. However, DATE was only added in SQL Server 2008, whereas the formula has compatibility back to at least SQL 2000. So if you need the backwards compatibility or are dealing with SQL CE, casting to DATE is unavailable.
SELECT *
FROM progen.DY
WHERE CONVERT(TIME, DY_DATE - CONVERT(DATE, DY_DATE)) > '00:00'