SQL Date Subtraction Between Two Years - sql-server

I am having an issue with a query I am trying to convert from MS Access. The query flags record for removal when it is older than 90 days but when I convert this query to sql server is is removing too many records.
UPDATE DT.SM_T_CountTotals
SET IsActive = 0
WHERE Convert(varchar, DT.SM_T_CountTotals.PostDate, 101) <
Convert(varchar, GetDate()- 90, 101)
When I run this query in MS Access I get a total of 3793 records that are flagged but in SQL server I get 69061 records that are flagged for removal. The GetDate()-90 value is correct at 10/26/2010 but it is flagging everything from this year to be removed.
I am sure it is something easy that I am overlooking. Help please?
I figured it out:
UPDATE DT.SM_T_CountTotals
SET IsActive = 0
WHERE DT.SM_T_CountTotals.PostDate < Convert(varchar, GetDate()- 90, 101)

You're comparing VARCHAR values, not DATEs.
101 converts to MM/DD/YY, so you're comparing month, then day, then year.
You should be using 112 (yymmdd)

Calculations between two dates can be easily done in the native data type rather than convert it to string. One can (and you have) get incorrect answers from such conversions.
Use DateDiff in the where clause to get the records that are more than 90 days old.
http://msdn.microsoft.com/en-us/library/ms189794.aspx
UPDATE DT.SM_T_CountTotals
SET IsActive = 0
WHERE ABS (DATEDIFF (dd, Getdate(), DT.SM_T_CountTotals.PostDate)) > 90

Related

Cannot construct data type date, some of the arguments have values which are not valid

I have a date in my database which should not have included the year, so I need to construct the correct date by checking the date. (If the date is yet to come this year, I want to set 2017, if the date has passed, I want to set 2018)
(t.myDate is a nullable datetime)
SELECT
NewDate = CASE
WHEN DATEPART(dayofyear, GETDATE()) < DATEPART(dayofyear, t.myDate)
THEN DATEFROMPARTS('2017', DATEPART(MONTH,t.myDate), DATEPART(DAY, t.myDate))
ELSE DATEFROMPARTS('2018', DATEPART(MONTH,t.myDate), DATEPART(DAY,t.myDate))
END
FROM
MyTable t
However, in some cases I get the following error message:
Cannot construct data type date, some of the arguments have values which are not valid.
I'm not sure what the problem is, but I know some rows are NULL. So I have tried to get around the problem by using the CASE expression to simply use today's date in the cases where the problem is:
SELECT
NewDate = CASE
WHEN t.myDate is null
THEN GETDATE()
WHEN DATEPART(MONTH,t.myDate) < 1
THEN GETDATE()
WHEN DATEPART(MONTH,t.myDate) > 12
THEN GETDATE()
WHEN DATEPART(DAY,t.myDate) < 1
THEN GETDATE()
WHEN DATEPART(DAY,t.myDate) > 31
THEN GETDATE()
WHEN ISDATE(t.myDate) = 0
THEN GETDATE()
WHEN DATEPART(dayofyear, GETDATE()) < DATEPART(dayofyear, t.myDate)
THEN DATEFROMPARTS('2017', DATEPART(MONTH,t.myDate), DATEPART(DAY, t.myDate))
ELSE DATEFROMPARTS('2018', DATEPART(MONTH,t.myDate), DATEPART(DAY,t.myDate))
END
FROM
MyTable t
But I still get the same error. Either I'm testing for the wrong data quality issues, or the CASE expression is not working as I expect it to.
Also: It does not help to use a where clause for NULL rows:
WHERE t.myDate IS NOT NULL
How can I find the data quality issue, and how can I get around it?
(I'm using SQL Server 2012)
Apart from checking for NULL you may also have a problem with 29th of February. If you for example have date 2016-02-29 and you are trying to construct date 2017-02-29 you will get above error since this is not a valid date.
In general you could use cursor to loop over records and execute your logic inside TRY/CATCH. On exception you could print offending data and check what the problem is.

SQL Server : BETWEEN DATES brings records outside the period

I am working with an application (EHR system) and needs to bring records with in the specific date range in SSRS record. In the script I format the date variable and applies to the column.
Convert(varchar(10), #begdt, 101) + ' 00:00:00' and
Convert(varchar(10), #enddt, 101) + ' 23:59:59'
If I set the begin and end date to 01/01/2017 and 01/31/2017, then it pulls the records that are associated with 02/01/2017. Can anyone help me with this?
Don't convert the dates to strings. When you do, you allow SQL Server to compare them alphabetically, rather than in actual date order.
Perhaps you have a language conflict (US vs UK) and SQL Server is in fact seeing that date as 2nd of January.
This post offers info on Language and Dates: (SQL Server Datetime issues. American vs. British?)
Don't use between, and set your date formats different:
#begdt = '20170101'
#enddt = '20170131'
WHERE column1 >= #begdt AND column2 < #enddt
This will take care of your datetime issue

SSRS graph: Retrieve values from different dates in SQL query

I am using the following query to retrieve snapshot values from 4 different dates: -1 day (latest), -2 days, -3 days and -40 days (not yet implemented).
SELECT [SnapshotDate]
,[SnapshotKey]
,[info1]
,[info2]
,[info3]
FROM [Database].[dbo].[Values]
WHERE [SnapshotDate] >= DATEADD(day,-3, GETDATE())
AND [SnapshotKey] = 'Some text here'
This results in the following graph:
The query is not quite right firstly since it is showing 4 values and should only be showing 3 at this point. Secondly I would like to show the last snapshot from 40 days ago as shown in the graph.
I have tried a few different queries but have not managed to figure out how to do this properly.
[SnapshotKey] = SELECT DATEADD(day,-40,getdate())
The above query gives me the correct answer in theory. However, when I use this in my query there is no result. I believe this might be due to not having a date conversion or the fact that I'm using "day" in my query. I'm not sure.
Any suggestions?
EDIT:
I also tried using the following with no luck (no result):
CONVERT(date, [SnapshotDate]) = CONVERT(date, DATEADD(day,-40, GETDATE()))
I'm not sure what your date values are but I'm guessing this report was run on 2nd May.
If this is correct then you need to change the range to exclude where the difference in dates is zero. Personally I use DATEDIFF in situations like this as it's easier to visualise for me.
Try changing the where clause to something like this.
WHERE (DATEDIFF(day, getdate(),[SnapshotDate]) BETWEEN -3 AND -1
OR DATEDIFF(day, getdate(), [SnapshotDate]) = -40)
AND [SnapshotKey] = 'Some text here'

Get Data From Last 48 Hours

I'm using PowerPivot with an SQL Server database and I'm working with a specific table that includes a DateTime column. I'd like to select the data from the table where that DateTime is within the last 48 hours. I'm using a query currently and hoping to achieve something like
... WHERE DT > DATE_SUB(CURRENT_TIMESTAMP, INTERVAL +2 DAY)
I'm getting a syntax error at DAY in this particular way. Is this the best way to do it? If so, what's wrong with how I've written it? If not, what's a better way?
Sql-Server
WHERE DateTimeColumn >= DATEADD(HOUR, -48, GETDATE())
Mysql
WHERE DateTimeColumn > DATE_SUB(NOW(), INTERVAL 48 HOUR)

Average a time value in SQL Sever 2005

I've got a varchar field in SQL Sever 2005 that's storing a time value in the format "hh:mm"ss.mmmm".
What I really want to do is take the average using the built in aggregate function of those time values. However, this:
SELECT AVG(TimeField) FROM TableWithTimeValues
doesn't work, since (of course) SQL won't average varchars. However, this
SELECT AVG(CAST(TimeField as datetime)) FROM TableWithTimeValues
also doesn't work. As near as I can tell, SQL doesn't know how to convert a value with only time and no date into a datetime field. I've tried a wide variety of things to get SQL to turn that field into a datetime, but so far, no luck.
Can anyone suggest a better way?
SQL Server can convert a time-only portion of a datetime value from string to datetime, however in your example, you have a precision of 4 decimal places. SQL Server 2005 only recognizes 3 places. Therefore, you will need to truncate the right-most character:
create table #TableWithTimeValues
(
TimeField varchar(13) not null
)
insert into #TableWithTimeValues
select '04:00:00.0000'
union all
select '05:00:00.0000'
union all
select '06:00:00.0000'
SELECT CAST(TimeField as datetime) FROM #TableWithTimeValues
--Msg 241, Level 16, State 1, Line 1
--Conversion failed when converting datetime from character string.
SELECT CAST(LEFT(TimeField, 12) as datetime) FROM #TableWithTimeValues
--Success!
This will convert valid values into a DATETIME starting on 1900-01-01. SQL Server calculates dates based on 1 day = 1 (integer). Portions of days are then portions of the value 1 (i.e. noon is 0.5). Because a date was not specified in the conversion, SQL Server assigned the value of 0 days (1900-01-01), which accommodates our need to average the time portion.
To perform an AVG operation on a DATETIME, you must first convert the DATETIME to a decimal value, perform the aggregation, then cast back. For example
SELECT CAST(AVG(CAST(CAST(LEFT(TimeField, 12) as datetime) AS FLOAT)) AS DATETIME) FROM #TableWithTimeValues
--1900-01-01 05:00:00.000
If you need to store this with an extra decimal place, you can convert the DATETIME to a VARCHAR with time portion only and pad the string back to 13 characters:
SELECT CONVERT(VARCHAR, CAST(AVG(CAST(CAST(LEFT(TimeField, 12) as datetime) AS FLOAT)) AS DATETIME), 114) + '0' FROM #TableWithTimeValues
Try this
AVG(CAST(CAST('1900-01-01 ' + TimeField AS DateTime) AS Float))
You really should store those in a datetime column anyway. Just use a consistent date for that part (1/1/1900 is very common). Then you can just call AVG() and not worry about it.
I used Cadaeic's response to get an answer I was looking for, so I thought I should share the code....
I was looking for a query that would average ALL my times together and give me an overall Turn Around Time for all approvals. Below is a nested statement that gives you the AVG TAT for individual id's and and when nested an overall TAT
SELECT
-- calculates overall TAT for ALL Approvals for specified period of time
-- depending on parameters of query
CONVERT(VARCHAR, CAST(AVG(CAST(CAST(LEFT(Tat_mins, 12) as datetime) AS FLOAT)) AS DATETIME), 108) + '0'
from
(
-- tat is for individual approvals
SELECT
dbo.credit_application.decision_status,
dbo.credit_application.application_id,
cast(dbo.credit_application.data_entry_complete as date) as'Data Entry Date',
cast(dbo.credit_application.decision_date as DATE) as 'Decision Date',
avg(datediff(minute, dbo.credit_application.data_entry_complete, dbo.credit_application.decision_date)) as 'TAT Minutes',
convert (char(5), DateAdd(minute, Datediff(minute,dbo.credit_application.data_entry_complete, dbo.credit_application.decision_date),'00:00:00'),108) as 'TAT_Mins'
FROM dbo.credit_application
where Decision_status not in ('P','N')
group by dbo.credit_application.decision_status,
dbo.credit_application.data_entry_complete,
dbo.credit_application.decision_date
--dbo.credit_application.application_id
)bb
How do you think to average on datetime?
I guess that you need to GROUP BY some period (Hour?), and display Count(*)?
SQL Server stores datetime data as 2 4-byte integers, hence a datetime take 8 bytes. The first is days since the base date and the second is milliseconds since midnight.
You can convert a datetime value to an integer and perform mathematical operations, but the convert only returns the "days" portion of the datetime value e.g. select convert(int,getdate()). It is more difficult to return the "time" portion as an integer.
Is using SQL Server 2008 an option for you? That version has a new dedicated time data type.
Thanks, Andy.
I'd work out the difference between all of the dates and an arbitrary point (01/01/1900), average it and then add it back on to the arbitrary point.

Resources