The accuracy of oracle when compare two date in SQL - sql

It is in two procedures which are running in order. In procedure one, the running timestamp of system time is 2019/5/30 12:02:58.100. The sql is as below
insert into A(xxx,xxx,cdate) values(xxx,xxx,sysdate)
And I find the cdate field of this inserted row is 2019/5/30 12:02:58, without the millisecond.
And then procedure two runs, the running timestamp of system time is 2019/5/30 12:02:58.200. The SQL is as below
select xxx from A where cdate<sysdate
This returns none result. It is weird because I just inserted a row with cdate 2019/5/30 12:02:58. This should be less than sysdate.
When Oracle stores date value like 2019/5/30 12:02:58, does it throw millsecond away or does it just store it in background and not show it?
While comparing cdate<sysdate, which two values does it use? I guess it is using 2019/5/30 12:02:58 < 2019/5/30 12:02:58, so this returns false.

When Oracle stores date value like 2019/5/30 12:02:58, does it throw millsecond away or does it just store it in background and not show it?
A date value does not have any millisecond component to throw away. sysdate returns a DATE data type:
This data type contains the datetime fields YEAR, MONTH, DAY, HOUR, MINUTE, and SECOND. It does not have fractional seconds or a time zone.
In your procedures you are looking at a TIMESTAMP value:
This data type contains the datetime fields YEAR, MONTH, DAY, HOUR, MINUTE, and SECOND. It contains fractional seconds but does not have a time zone.
... or a variant that does have a time zone, like TIMESTAMP WITH TIME ZONE, as returned by systimestamp:
This data type contains the datetime fields YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, TIMEZONE_HOUR, and TIMEZONE_MINUTE. It has fractional seconds and an explicit time zone.
While comparing cdate<sysdate, which two values does it use? I guess it is using 2019/5/30 12:02:58 < 2019/5/30 12:02:58, so this returns false.
As your insert statement is using sysdate, it doesn't really matter at this point whether cdate is defined as DATE or TIMESTAMP, if the latter then the fraction seconds part is just truncated to zero. And sysdate also returns a DATE, so yes, it is either doing:
2019-05-30 12:02:58 < 2019-05-30 12:02:58
or
2019-05-30 12:02:58.000 < 2019-05-30 12:02:58
which may involve an implicit cast. Either way the result is indeed false.
If you want to compare values that might be within the same second then you have to use a TIMESTAMP. Define your column as one of the TIMESTAMP variants as appropriate, and then use systimestamp instead of sysdate both for your insert and for the comparison. It all has to be timestamps - if any part of what you do stays as a date then at some point the fractional seconds will be lost and you'll be in the same position you are now.

If your column is a DATE type then it doesn't store milliseconds. SYSDATE doesn't return them either. Ensure your column is a TIMESTAMP with a suitable number for fractional precision to store the milliseconds you want, and ensure you're comparing it with SYSTIMESTAMP

Related

Convert sysdate to BST(British Summer Time)

I have a Java api which gets the data from oracle database which is in ET timezone. I want to query that table using sysdate on 2 columns but the sysdate should be picked as current BST date value not as ET date value.
select * from customers where sysdate between mem_registered_date and mem_deregistered_date;
How this can be done? please help
The simplest way to convert between time zones is to use a data type that understands time zones, a TIMESTAMP. Then when you've converted to the time zone you want then CAST it back to a DATE data type:
SELECT *
FROM customers
WHERE CAST( SYSTIMESTAMP AT TIME ZONE 'Europe/London' AS DATE )
BETWEEN mem_registered_date AND mem_deregistered_date;
I'm assuming that you want the current time in the United Kingdom (BST in summer and GMT in winter), if you want the time zone UTC+1 then use:
CAST( SYSTIMESTAMP AT TIME ZONE '+01:00' AS DATE )
If querying a column with a date-only value, without any time-of-day nor any time zone, that is, a column of a type akin to the SQL-standard DATE type, then use Java class LocalDate.
Generally best to use half-open definition of a span of time, where the beginning is inclusive while the ending is exclusive. This allows spans to nearly abut one another without gaps or overlap. So never use the SQL command BETWEEN for date-time ranges, as it is fully-closed (both beginning and ending are inclusive).
Get today’s date as seen in the wall-clock time used by the people of a particular region (a time zone).
BST is not a real time zone. For British time, use Europe/London.
ZoneId z = ZoneId.of( "Europe/London" ) ;
LocalDate today = LocalDate.now( z ) ;
The SQL will look something like this:
SELECT *
FROM event_
WHERE ? >= start_
AND ? < end_
;
Fill in the placeholders.
myPreparedStatement.setObject( 1 , today ) ;
myPreparedStatement.setObject( 2 , today ) ;
Load the date values from database into Java.
LocalDate start = myResultSet.getObject( … , LocalDate.class ) ;
TIMESTAMP WITHOUT TIME ZONE
The misnamed DATE in the Oracle database actually represents a date with time-of-date without the context of a time zone or offset-from-UTC. As such, this type cannot represent a moment, a specific point on the timeline. It the value is noon on the 23rd of January next year, we cannot know if that is noon in Tokyo, Toulouse, or Toledo — all different moments several hours apart. This DATE type is akin to the SQL-standard type TIMESTAMP WITHOUT TIME ZONE.
So for this data type, your question asking about time zones makes no sense. Apple and oranges. Involving time zones means you are tracking moments, specific points on the timeline. But the Oracle DATE cannot represent moments as discussed above.
To track moments, your should be using a column of a type akin to the SQL-standard TIMESTAMP WITH TIME ZONE.

google bigquery select from a timestamp column between now and n days ago

I have a dataset in bigquery with a TIMESTAMP column "register_date" (sample value "2017-11-19 22:45:05.000 UTC" ).
I need to filter records based on x days or weeks before today criteria.
Example query
select all records which are 2 weeks old.
Currently I have this query (which I feel like a kind of hack) that works and returns the correct results
SELECT * FROM `my-pj.my_dataset.sample_table`
WHERE
(SELECT
CAST(DATE(register_date) AS DATE)) BETWEEN DATE_ADD(CURRENT_DATE(), INTERVAL -150 DAY)
AND CURRENT_DATE()
LIMIT 10
My question is do I have to use all that CASTing stuff on a TIMESTAMP column (which seems like over complicating the otherwise simple query)?
If I remove the CASting part, my query doesn't run and returns error.
Here is my simplified query
SELECT
*
FROM
`my-pj.my_dataset.sample_table`
WHERE
register_date BETWEEN DATE_ADD(CURRENT_DATE(), INTERVAL -150 DAY)
AND CURRENT_DATE()
LIMIT
10
that results into an error
Query Failed
Error: No matching signature for operator BETWEEN for argument types: TIMESTAMP, DATE, DATE. Supported signature: (ANY) BETWEEN (ANY) AND (ANY) at [6:17]
any insight is highly appreciated.
Use timestamp functions:
SELECT t.*
FROM `my-pj.my_dataset.sample_table` t
WHERE register_date BETWEEN TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL -150 DAY) AND CURRENT_TIMESTAMP()
LIMIT 10;
BigQuery has three data types for date/time values: date, datetime, and timestamp. These are not mutually interchangeable. The basic idea is:
Dates have no time component and no timezone.
Datetimes have a time component and no timezone.
Timestamp has both a time component and a timezone. In fact, it represents the value in UTC.
INTERVAL values are defined in gcp documentation
Conversion between the different values is not automatic. Your error message suggests that register_date is really stored as a Timestamp.
One caveat (from personal experience): the definition of day is based on UTC. This is not much of an issue if you are in London. It can be a bigger issue if you are in another time zone and you want the definition of "day" to be based on the local time zone. If that is an issue for you, ask another question.

SQL : How sysdate minus a value works?

I found below code in my existing project.
select * from mytable where SomeColumn_date >= trunc(sysdate)-.25/24;
Sample value for SomeColumn_date is 22-JUN-17 05:46:55
How does SomeColumn_date >= trunc(sysdate)-.25/24 work on Date data type?
Different database engines allow different operations to be applied to date data types. For the most part, an operation of <Date Value> +/- 1 will add or subtract one day to that date value. This is syntactically equivalent to the dateadd function when using days.
In your example here, the -.25/24 resolves to the number equivalent of -15 minutes, which is then subtracted from your date value.
It is essentially a much less readable version of datedd(min,-15,<Date Value>).
From the documentation of TRUNC (I'm guessing you are using Oracle):
The TRUNC (date) function returns date with the time portion of the day truncated to the unit specified by the format model fmt. [...] If you omit fmt, then date is truncated to the nearest day.
The result of trunc(sysdate) would be the present date without the time component. Now .25/24 (actually meaning 0.25/24) is substracted from that. If you substract a date using - the operand is always in days. 0.25/24 would be a form to express a quarter of an hour.
So trunc(sysdate)-.25/24 would result in yesterday 23:45.
Ok so 2 things are happening here:
trunk(date,fmt)
The TRUNC (date) function returns date with the time portion of the day truncated to the unit specified by the format model fmt. If you omit fmt, then date is truncated to the nearest day.
So if you have suppose 22-JUN-17 05:46:55 you get 22-JUN-17. Since you don't have the fmt
DATETIME - .25/24 implies .25 hours before your current Date time.
But since you have only DATE all it does is .25 hours before todays 12:00 AM i.e yesterdays 11:45PM
SomeColumn_date >= trunc(sysdate)-.25/24
So suppose if its 22-JUN-2017 right now the date is compared to 21-JUN-2017 11:45 PM
NOTE: - is for before current time, + is for after the current time

Substraction with decimal in ORACLE SQL

I need to substract 2 timestamps in the given format:
16/01/17 07:01:06,165000000
16/01/17 07:01:06,244000000
I want to express the result with 2 decimal values but somewhere in the CAST process I am loosing precision. My atempt by now goes this way:
select
id,
trunc((CAST(MAX(T.TIMESTAMP) AS DATE) - CAST(MIN(T.TIMESTAMP) AS DATE))*24*60*60,2) as result
from table T
group by id;
But I get id_1 '0' as a result for the two timestamps above even after I set the truncate decimals at 2.
Is there a way that I can obtain the 0.XX aa a result of the substraction?
It's because you are casting the timestamp to date.
Use to_timestamp to convert your string into timestamp.
Try this:
with your_table(tstamp) as (
select '16/01/17 07:01:06,165000000' from dual union all
select '16/01/17 07:01:06,244000000' from dual
),
your_table_casted as (
select to_timestamp(tstamp,'dd/mm/yy hh24:mi:ss,ff') tstamp from your_table
)
select trunc(sysdate + (max(tstamp) - min(tstamp)) * 86400 - sysdate, 2) diff
from your_table_casted;
The difference between two timestamps is INTERVAL DAY TO SECOND.
To convert it into seconds, use the above trick.
DATE—This datatype stores a date and a time, resolved to the second. It does not include the time zone. DATE is the oldest and most commonly used datatype for working with dates in Oracle applications.
TIMESTAMP—Time stamps are similar to dates, but with these two key distinctions: you can store and manipulate times resolved to the nearest billionth of a second (9 decimal places of precision), and you can associate a time zone with a time stamp, and Oracle Database will take that time zone into account when manipulating the time stamp.
The result of a substraction of two timestamps is an INTERVAL:
INTERVAL—Whereas DATE and TIMESTAMP record a specific point in time, INTERVAL records and computes a time duration. You can specify an interval in terms of years and months, or days and seconds.
You can find more information here

inserting current system time in table through a keyword using sql query

just like sysdate inserts the current date,
Is there any way to insert only the current time using a sql query.
for example *insert into table_name values(sysdate); *
There is no Oracle data type that represents just a hours/minutes/seconds. There is DATE and the various TIMESTAMP types that represents a point in time, including date and time. You don't say why you're trying to store this, but you could store the time as an offset in a NUMBER column, as fractions of a day.
So, if you wanted to find store the time of day that a store opens on Monday, you'd do:
TRUNC( SYSDATE ) + offset