calculating duration (Date - Date) from a case Expression - sql

I'm writing a complex query and I'm having some problems with calculating from case expression (thank you). Essentially, thanks to some great help on this site I managed to solve another issue by using an ordered table but now one of the select statements I was previously using no longer functions with the OT.
I get an error:
Conversion failed when converting date and/or time from character string
The core of the problem is that I want to calculate duration from subtracting a start date from an end date. The code I had previously used was
select
cast((CASE
WHEN ap.startLIKE '%[^0-9]%'
THEN CAST(ap.end as datetime)
ELSE NULL
END)
-
(CASE
WHEN ap.start LIKE '%[^0-9]%'
THEN CAST(ap.start as datetime)
ELSE NULL
END) as time) as Duration,
ap.start, ap.end, ap.description
from
Table as AP
group by
ap.start, ap.end, ap.description
but when I changed to draw this information from the ordered table as:
Select
ot.starttime as "Start Time",
ot.endtime as "End Time",
ot.description "Description",
ot.entrynumber as "ID Number",
cast((CASE
WHEN ot.endtime LIKE '%[^0-9]%'
THEN CAST(ot.endtime as datetime)
ELSE NULL
END)
-
(CASE
WHEN ot.starttime LIKE '%[^0-9]%'
THEN CAST(ot.starttime as datetime)
ELSE NULL
END) as time) as duration
From
OrderedTable as OT
group by
ot.starttime, ot.endtime, ot.description, ot.entrynumber
order by
ot.endtime
I get the error. Previously applying the group by clause had removed the error, but not from the ordered table.
Genuinely unsure what is causing the problem as I've been trying to eliminate the conversion to date issue constantly, I would appreciate any help.
The originating problem has been that the date fields on the server are in nvarchar format and not datetime, I've been trying to get around that.

If I understand your question correctly - you can use
TIMEDIFF(date1, date2)
or TIMESTAMPDIFF(SECOND, date2, date1);
where date2 < date1 to find the time between two dates. In the first case you will get the result in HH:mm:ss format and in the second case it will be in seconds - depends what you need. Since you didn't post an example how a date looks in your column I can't be sure, but I think you wont need conversions - you can substract your dates as chars (If they look this way '2016-12-31 11:02:00')

Since the column was formatted in nvarchar32 rather than date/time which it should have been, it resulted in periodically the database receiving a text value for (some) open records for the finish time rather than a NULL value.
This occurred only periodically and for whatever reason proposed by whoever designed the database.
This ideally shouldn't happen since its against database convention to do this kind of thing, as NULL values are simply ignored by calculation functions since they are unknown. Still unclear why the case expression didn't resolve the issue, but using the filter FinishTime <> "error text", I resolved the problem.

Related

Case expression - comparing dates

I am using the query below, if the deadline column (eh.Deadline) < today (getdate) then returns 1, else 0.
(case when he.Deadline < CONVERT(VARCHAR(10),GETDATE(),111) then 1 else 0 end) Deadline
Everything works fine, but I need to change it so that only the month and the year (not the day), are compared.
Example, if Deadline=10/01/2022 and today=11/01/2022
My query returns 1 (10/01/2022 < 11/01/2022)
I would like to compare only the month and the year - I would like it to return 0 as MM/YYYY it's the same (01/2022 = 01/2022)
EDIT:
The column he.Deadline has the following format: DD/MM/YYYY HH/MM and this could not be compared to GETDATE and to solve that I had to use CONVERT(VARCHAR(10),GETDATE(),111) instead of GETDATE
The above query is used for a dashboard I created, and I wouldn't want to change anything in the database.
All I want is to compare MM/YYYY instead of DD/MM/YYYY
Any ideas please?
Thank you very much!
you could do (case when EOMONTH(he.Deadline) < EOMONTH(GETDATE()) then 1 else 0 end) Deadline
set the date to last day of the month for both values before comparing them so only the month/year will matter in the end
edit: as long as Deadline is a date... if it isn't then you have to convert it to date before calling EOMONTH on it
Firstly you need to ensure you are comparing a datetime/date with a datetime/date not a string with a datetime, nor a string with a string, as the comparisons are different all different, and your desired result is comparing a date.
Then you should be storing your data in the correct datatype, which for a datetime is a datetime2. Storing your Deadline as a string is going to cause you pain and trouble for the life of the project and will perform badly.
However with the situation as it is, you need to first convert your Deadline column correctly into a date value, and then secondly one way to solve your actual problem is to compare to the first of the current month as follows:
select
-- Convert to a date datatype in order to be able to correctly compare to another date
convert(date,substring(he.Deadline,1,10),103) -- Ensure dd/mm/yyyy as opposed to mm/dd/yyyy
<
-- Calculate the first of the month and check whether the Deadline is before then
convert(date,dateadd(day, -1*(datepart(day,getdate())-1), getdate())) then 1 else 0 end Deadline
from (
values ('10/01/2022 12/30')
) he (Deadline)

Date invalid when no match is found in DB2

I'm running a very simple DB2 query which is giving me a timestamp error because it can't find records with 2019-02-29 and so it's returning an invalid date type. If I change it to 2019-02-28 then it works.
I can't seem to find a proper "CASE WHEN" use for this, but is there a simple way to not interfere with current functionality but to say "If this date isn't found just ignore today's date"?
I need it to run even if there are nor prior year records for 02-29
SELECT 'DATE' AS RANGE, COUNT(*) AS COUNT
FROM datesTable
WHERE user = 123
AND newDate BETWEEN '2019-01-01' AND '2019-02-29'
I think a case should work:
newDate BETWEEN '2019-01-01' AND
(CASE WHEN '2019-02-29' <> '2019-02-29' THEN '2019-02-29' END)
This assumes that you are constructing the value somehow, based on the current date.
The problem is because the column is a date so the operands to BETWEEN are all converted to dates. The case comparison should get around this, by only converting when the data is not the forbidden date.
Or, do the comparisons as strings:
where varchar_format(newDate, 'YYYY-MM-DD') between '2019-01-01' AND '2019-02-29'
No conversion to date is happening here, so no error should occur. That said, this will prevent the use of an index on newDate.

Casting Strings into Birthdates (that aren't in the future)

I am casting mm/dd/yy strings into dates in redshift using CAST AS DATE CAST(birth_str AS DATE) AS birth_date. The conversion handles the components correctly but the year is being converted into future times whenever it falls below 1970. For example:
birth_str birth_date
07/19/84 1984-07-19
02/07/66 2066-02-07
06/24/84 1984-06-24
01/31/64 2064-01-31
12/08/62 2062-12-08
02/21/36 2036-02-21
02/19/37 2037-02-19
07/01/74 1974-07-01
08/25/50 2050-08-25
08/31/39 2039-08-31
Is there a best practice for getting dates to not fall into the future?
Is there not an argument for this in the cast? (I looked everywhere but I am finding nothing.) Otherwise, I am envisioning the best path forward is testing for the cast date being in the future and then just doing string surgery on the miscreants before recasting them into reasonable dates.
Basically:
if not future date: great.
if future date:
peel out all the date components
slap a 19 onto the yy
glue everything back together
cast into date.
Is this as good as it gets? (I was a bit surprised I could find no one has come up with a better way around this issue already.)
Is there a best practice? Absolutely! Don't store dates as strings. Store dates as date. That is why SQL has native types.
In your case, you could use conditional logic:
select (case when cast(birth_str AS DATE) < current_date
then cast(birth_str AS DATE)
else cast(birth_str AS DATE) - interval '100 year'
end) as birth_date
Or since Redshift can't handle intervals you can go with this:
SELECT (CASE
WHEN birth_str::DATE < CURRENT_DATE
THEN birth_str::DATE
ELSE ADD_MONTHS(birth_str::DATE, -1200)
END) AS birth_date
You can apply a CASE to check the converted DATE IS greater than TODAY or not. If Yes, Just minus 100 years from the results as below.
One Question: Is there any chance of having dates like 02/21/14 which can be belongs to 1900 or 2000?
SELECT
CASE
WHEN CAST('02/21/36' AS DATE) >GETDATE() THEN DATEADD(YY,-100,CAST('02/21/36' AS DATE))
ELSE CAST('02/21/36' AS DATE)
END

Using GETDATE() to pull from a table

My apologies in advance for the basic (and probably silly) question.
I'm doing a SELECT query where I'm joining several tables. I have a table that contains a numeric value associated with certain days. What I would like to have in my output is the numeric value attached to today, but I'm clueless as to how to make this happen. Obviously, I would have the same value for every record in my output and I'm fine with that.
Is this even possible? If so, what would an example of the code be?
The table from which I would like the numeric value simply has dates in one column ([Calendar_Day]) and integers in another column ([X_Value]).
Below, you can see the last three lines of my SELECT statement. This latest attempt yielded NULL.
,[EnterpriseDB].[pcc].[conditions].Internal
,GETDATE() AS Date_Today
,(SELECT [X_Value] FROM [Analytics].[WorkArea].[Days_Vals] WHERE [Calendar_Day] = GETDATE()) AS BizVal_Today
Just guessing:
[Calendar_Day] is of the type date. While getdate() returns a datetime. That means the DBMS upcasts [Calendar_Day] to a datetime with a time of 00:00:00.0 in order to be able to compare the operands. But getdate() includes the current time. So unless you're executing the query at exactly 00:00:00.0 the values won't match and the subquery returns null.
To fix that, downcast getdate().
... WHERE [Calendar_Day] = convert(date, getdate()) ...
If [Calendar_Day] is also a datetime but you don't want to compare the hour (, minute, second ...) portion of it, downcast it as well.
... WHERE convert(date, [Calendar_Day]) = convert(date, getdate()) ...
Might throw any indexes out of the window though, if there are some on [Calendar_Day]. You might want to consider changing the data type to date or using a persistent, indexed computed column and compare against that, if that leads to any performance issues.

XCODE - SQL Dates

I am trying to update my SQL DB when the the date has expired (i.e is less than now)
I have this lovely bit of SQL code
Update Notifications
SET Active = 'N'
where CAST(SetDate AS DATE) <= CAST('2012-08-23 11:19:00 +0000' AS DATE)
But it updates all the records (even if the date is not less than now)
I have also tried
Update Notifications
SET Active = 'N'
where CAST(SetDate AS DATE) < CAST('2012-08-23 11:19:00 +0000' AS DATE)
But this dosent affect any rows.
I guess I have something a little confused??
Any help???
Thanks
I cannot see anything odd about the code, except for this line:
CAST('2012-08-23 11:19:00 +0000' AS DATE)
If you are using CAST() to change it to the date, then there is no need to pass in the time portion of the value.
You did not provide the full table schema but one thing to consider is using a bit field for the Y/N values.
Here is a SQL Fiddle with the code working
Update Notifications
SET Active = 'N'
where CAST(SetDate AS DATE) <= CAST('2012-08-23' AS DATE)
One thing you have confused is types in SQL. I don't know what database you are using, but it probably has types for boolean (or int) and date. Storing booleans and dates as strings is very inefficient. For example, even if your example did work it would have to scan the entire database and cast each value in order to compare it.
The clue to the error is the < vs <=. This hints at the values being equal. If the cast fails on both sides, and returns the default value of zero, then then will be equal.
You should change your schema to use the correct types, then your query will work.