How can I compute a duration from two dates? - sql

I have this query, that's been giving me some issues, it looks like this:
UPDATE servicecontracts
SET planned_duration = (to_char(due_date) - to_char(start_date) + 1)
,actual_duration =''
,progress = NULL
WHERE servicecontractsid = '263'
After some research, I managed to figure out what this query is trying to do, it' s just trying to find the planned duration, by subtracting the due date and the start date. Why, this is trying to do that by subtracting strings, I do not know. Also, the to_char function requires a second parameter.
So, anyway, now I need to find the planned_duration, but how do I do that. According to the Postgresql docs, the to_char function doesn't have an option to return an integer, if you set it to return text and then if you try to convert the string into an integer using explicit casts, like ::integer, you get an error because an integer can't have colons in there.
So, is there a way for to_char to return an integer that somehow represents the date, and then subtract the two?
If not, what should I do to carry this out?

I quote from the fine manual here
SELECT EXTRACT(EPOCH FROM TIMESTAMP WITH TIME ZONE '2001-02-16 20:38:40.12-08');
Result: 982384720.12
But for computing intervals, there is simpler way:
SELECT (due_date - start_date)

Just subtracting two date types from each other should return their difference in days.
SET planned_duration = (due_date - start_date)
Not sure why to_char is being used, unless I'm missing something here.

Related

Comparing two dates in Oracle after using TO_DATE function

I have the following Oracle Query that is converting todays date and the date field from the table into the same format. However, when trying to compare the two they aren't coming up as equal.
CAST(dstamp As Date), TO_DATE(CURRENT_DATE,'dd-MON-YY HH24.MI.SS','NLS_DATE_LANGUAGE = American')
The cast is used on the field in my table, both these return.
However, when adding the following where statement no rows are returned. I can't work out why these wouldn't be classed as equal?
WHERE CAST(dstamp As Date) = TO_DATE(CURRENT_DATE,'dd-MON-YY HH24.MI.SS','NLS_DATE_LANGUAGE = American');
Any help appreciated.
If you are trying to check if dstamp belongs to the current day, I would suggest:
where dstamp >= trunc(sysdate) and dstamp < trunc(sysdate) + 1
Athough a bit more verbose, this will be more efficient than applying a date function on the column being compared. Using a function on a column in a predicate makes the query non-SARGable, ie it cannot take advantage of an existing index.
The date is ALREADY a date. You don't need to convert it. You may need to remove the time component. Does this do what you want?
WHERE TRUNC(dstamp) = TRUNC(sysdate)

How to convert an Epoch timestamp to a Date in Standard SQL

I didn't find any simple answer to this while I was looking around, so I thought I'd put it up here in case anyone was having the same problem as me with what should have been a trivial issue.
I was using ReDash analytics with Google's BigQuery and had turned on Standard SQL in the datasource settings. For the purposes of my query, I needed to convert a timestamp - unix time in milliseconds, as a string - to a Date format so that I could use the DATE_DIFF method.
As an example... "1494865480000" to "2017-05-15"
The difficulty was that casting and conversion was excessively strict and there seemed no adequate way to make it parse. See my answer down below!
(Though let me know if some SQL sensei knows a more eloquent way!)
In Standard SQL use TIMESTAMP_MICROS function together with EXTRACT(DATE FROM <timestamp>):
SELECT EXTRACT(DATE FROM TIMESTAMP_MILLIS(1494865480000))
A simpler way with TIMESTAMP_MILLIS():
#standardSQL
SELECT DATE(TIMESTAMP_MILLIS(CAST("1494865480000" AS INT64)))
2017-05-15
After much trial and error, this was my solution:
DATE_ADD( DATE'1970-01-01', INTERVAL CAST( ( CAST( epochTimestamp AS INT64 ) / 86400000 ) AS INT64 ) DAY ) AS convertedDate
That is, I took the string, cast it to an integer, divided it by the number of milliseconds in a day, then used a DATE_ADD method, and added the result to the start of Epoch time, and calculated the resulting day.
I hope this saves another junior some time!
Use UTC_USEC_TO_TIMESTAMP():
select UTC_USEC_TO_TIMESTAMP(postedon * 1000)
You can then extract the date using date():
select DATE(UTC_USEC_TO_TIMESTAMP(postedon * 1000))
This doesn't require knowing the internal format of Unix timestamps.

Jasper - Oracle - How to filter by date

I am trying to filter a query by date.
I have this line in my query:
AND (the_date like CONCAT (TO_DATE($P{THE_DATE}, 'YYYY-MM-DD'),'%'))
However, I just cannot seem to compare the parameter date to the database date effectively.
Database date is of type DATE. Parameter is of type String.
I've also tried:
AND (TO_CHAR(the_date) like CONCAT ($P{THE_DATE,'%')
Are my data types wrong? I've tried others but to no avail. Is my query wrong?
I'm using iReport... I looked for some kind of debugging option to see what is actually being executed but didn't find any.
When you want to compare DATEs, you need to convert the literal into DATE using TO_DATE.
No need to use LIKE operator. You could either useTRUNC on the DATE column, however, that would suppress any regular index usage. It would be better to use a DATE RANGE condition.
Remember, DATE has both date and time elements.
For example,
WHERE
the_date >= TO_DATE('14-MAY-2015','DD-MON-YYYY')
AND
the_date < TO_DATE('14-MAY-2015','DD-MON-YYYY') +1;
Instead of literals in above example, you could use your INPUT parameter or the local variable which you have defined as string.
WHERE
the_date >= TO_DATE(in_date,'DD-MON-YYYY')
AND
the_date < TO_DATE(in_date,'DD-MON-YYYY') +1;
The format stored in the databases was like 12/MAY/15.
Although I am convinced I attempted this in the input parameter which was of type String, this proved to be the magic answer :) my input was '12/MAY/15' and it worked.

Unknown SQL coding issue in Oracle SQL Developer

I'm writing an SQL statment that is supposed to do a count based on a date range. But, for some reason no data is being returned. Before I try and filter the count with my date range, everything works fine. Here is that code.
SELECT
CR.GCR_RFP_ID
,S.RFP_RECEIVED_DT
,CR.GCR_RECEIVED_DT
,CT.GCT_LOB_IND
FROM ADM.GROUP_CHANGE_TASK_FACT CT
JOIN ADM.B_GROUP_CHANGE_REQUEST_DIM CR
ON CR.GROUP_CHANGE_REQUEST_KEY = CT.GROUP_CHANGE_REQUEST_KEY
JOIN ADM.B_RFP_WC_COVERAGE_DIM S
ON S. RFP_ID = CR.GCR_RFP_ID
WHERE CT.GCT_LOB_IND = 'WC'
AND CR.GCR_CHANGE_TYPE_ID IN ('10','20','30','50','60','70','80','90','100','110',
'120','130','140', '150','160','170','180','190','200',
'210','220','230','240','260','270','280','300','310',
'320','330','340','350','360','370','371','372')
AND S.RFP_AUDIT_IND = 'N'
AND S.RFP_TYPE_IND = 'A'
The date field I'm using is called CR.GCR_RECIEVED_DT. This is a new field a in the db and all the records are 01-JAN-00. But I'm still doing the count just to make sure I can grab the data. Now, I added this line:
AND CR.GCR_RECEIVED_DT LIKE '01-JAN-00'
just as a random test thing. I know all the dates are the same. And it works fine, no issues. So I remove that line and replace it with this:
AND CR.GCR_RECEIVED_DT BETWEEN '31-DEC-99' AND '02-JAN-00'
I used this small range to keep it simple. But even though 01-JAN-00 deffinetly falls between those two dates, no data is returned. I have no idea why this is happening. I even tried this line to:
AND CR.GCR_RECEIVED_DT = '01-JAN-00'
and I still don't get data returned. It only seems to work with LIKE. I have checked and the field is a date type. Any help wold be much appreciated.
If your NLS_DATE_FORMAT is set to DD-MON-YY then the apparent discrepancy between the first two results can be explained.
When you use LIKE it implicitly converts the date value on the left-hand side to a string for the comparison, using the default format model, and then compares that to the fixed string; and '01-JAN-00' is like '01-JAN-00'. You're effectively doing:
AND TO_CHAR(CR.GCR_RECEIVED_DT, 'DD-MON-YY') LIKE '01-JAN-00'
Using LIKE to compare dates doesn't really make any sense though. When you use BETWEEN, though, the left-hand side is being left as a date, so you're effectively doing:
AND CR.GCR_RECEIVED_DT BETWEEN TO_DATE('31-DEC-99', 'DD-MON-YY')
AND TO_DATE('02-JAN-00', 'DD-MON-YY')
... and TO_DATE('31-DEC-99', 'DD-MON-YY') is December 31st 2099, not 1999. BETWEEN only works when the first value is lower than the second (from the docs, 'If expr3 < expr2, then the interval is empty'). So you're looking for values bwteen 2099 and 2000, and that will always be empty. If your date model was DD-MON-RR, from the NLS parameter or explicitly via TO_DATE, then it would be looking for values between 1999 and 2000, and would find your records.
Your third result is a little more speculative but suggests that the values in your GCR_RECEIVED_DT field have a time component, or are not in the century you think. This is similar to the LIKE version, except this time the fixed string is being converted to a date, rather than the date being converted to a string; effectively:
AND CR.GCR_RECEIVED_DT = TO_DATE('01-JAN-00', 'DD-MON-YY')
If they were at midnight on 2000-01-01 this would work. Because it doesn't that suggests they are either some time after midnight, or maybe more likely - since you're using a 'magic' date in your existing records - they are another date entirely, quite possibly 1900-01-01.
Here are SQL Fiddles for just past midnight and 1900.
If the field will eventually have a time component for new records you might want to structure the condition like this, and use date literals to be a bit clearer (IMO):
AND CR.GCR_RECEIVED_DT >= DATE '2000-01-01'
AND CR.GCR_RECEIVED_DT < DATE '2000-01-02'
That will find any records at any time on 2000-01-01, and can use an index on that column if one is available. BETWEEN is inclusive, so using BETWEEN DATE '2000-01-01' AND '2000-01-02' would include any records that are exactly at midnight on the later date, which you probably don't want.
Whatever you end up doing, avoid relying on implicit conversions using NLS_DATE_FORMAT as one day it might not be set to what you expect, causing potentially data-corrupting or hard to find bugs; and specify the full four-digit year in the model if you can to avoid ambiguity.
try something like this:
WHERE TRUNC(CR.GCR_RECEIVED_DT) = TO_DATE('01-JAN-00','DD-Mon-YY')
TRUNC without parameter removes hours, minutes and seconds from a DATE.

Date range comparison using varchar columns in Teradata

I've been tasked to take a calendar date range value from a form front-end and use it to, among other things, feed a query in a Teradata table that does not have a datetime column. Instead the date is aggregated from two varchar columns: one for year (CY = current year, LY = last year, LY-1, etc), and one for the date with format MonDD (like Jan13, Dec08, etc).
I'm using Coldfusion for the form and result page, so I have the ability to dynamically create the query, but I can't think of a good way to do it for all possible cases. Any ideas? Even year differences aside, I can't think of anything outside of a direct comparison on each day in the range with a potential ton of separate OR statements in the query. I'm light on SQL knowledge - maybe there's a better way to script it in the SQL itself using some sort of conversion on the two varchar columns to form an actual date range where date comparisons could then be made?
Here is some SQL that will take the VARCHAR date value and perform some basic manipulations on it to get you started:
SELECT CAST(CAST('Jan18'||TRIM(EXTRACT(YEAR FROM CURRENT_DATE)) AS CHAR(9)) AS DATE FORMAT 'MMMDDYYYY') AS BaseDate_
, CASE WHEN Col1 = 'CY'
THEN BaseDate_
WHEN Col1 = 'LY'
THEN ADD_MONTHS(BaseDate_, -12)
WHEN Col1 = 'LY-1'
THEN ADD_MONTHS(BaseDate_, -24)
ELSE BaseDate_
END AS DateModified_
FROM {MyDB}.{MyTable};
The EXTRACT() function allows you to take apart a DATE, TIME, or TIMESTAMP value.
You have you use TRIM() around the EXTRACT to get rid of the whitespace that is added converting the DATEPART to a CHAR data type. Teradata is funny with dates and often requires a double CAST() to get things sorted out.
The CASE statement simply takes the encoded values you suggested will be used and uses the ADD_MONTHS() function to manipulate the date. Dates are INTEGER in Teradata so you can also add INTEGER values to them to move the date by a whole day. Unlike Oracle, you can't add fractional values to manipulate the TIME portion of a TIMESTAMP. DATE != TIMESTAMP in Teradata.
Rob gave you an sql approach. Alternatively you can use ColdFusion to generate values for the columns you have. Something like this might work.
sampleDate = CreateDate(2010,4,12); // this simulates user input
if (year(sampleDate) is year(now())
col1Value = 'CY';
else if (year(now()) - year(sampleDate) is 1)
col1Value = 'LY'
else
col1Value = 'LY-' & DateDiff("yyyy", sampleDate, now());
col2Value = DateFormat(sampleDate, 'mmmdd');
Then you send col1Value and col2Value to your query as parameters.