Oracle vs. Hypersonic SQL - sql

I need to select by date in a SQL query, for example
SELECT * FROM foo WHERE date = '2009-09-09'
That query works in my Hypersonic test database, but not Oracle, which seems to requires:
SELECT * FROM foo WHERE date = TO_DATE('2009-09-09', 'yyyy-mm-dd')
Is there a way to select by date uniformly across these two databases?

I found the answer - you can create the TO_DATE function in HyperSonic and then the second query works in both. For example, make the class:
public class Date {
public static String toDate( String value, String format ) {
return value;
}
}
And the query
SELECT * FROM foo WHERE date = TO_DATE('2009-09-09', 'yyyy-mm-dd')
works in both.

You could try H2 database as your in memory database (http://www.h2database.com). It should have decent Oracle compablity mode.

HSQLDB 2.0 supports ANSI date literals just as Oracle. So if you can upgrade to HSQLDB 2.0, you can use:
SELECT *
FROM foo
WHERE date_column = DATE '2009-09-09'
in both database (actually a lot more databases even)

A "date = 'literal string'" predicate in Oracle is usually not recommended - it is sensitive to NLS_DATE_FORMAT settings and often leads to misunderstanding on what you're looking for in a result set (in your example above do you want all records for the day or just those created exactly at midnight?)
If you need a uniform query string for both databases, you might rename the table in Oracle and create a view with the name foo and cast the date datatype to varchar2 in the view logic. You'll probably need to add a function-based index to the table to allow efficient searching on the recast value.

If you can, you can set your NLS_DATE_FORMAT in Oracle session, that way you do not need to use the TO_DATE function, oracle will do this for you behind the scenes.
SQL> select value from v$nls_parameters where parameter = 'NLS_DATE_FORMAT';
VALUE
----------------------------------------------------------------
DD/MM/YYYY
SQL> create table nls_date_test ( id number(10) , date_entered date );
Table created.
SQL> insert into nls_date_test values ( 1 , '31/05/2009' );
1 row created.
SQL> insert into nls_date_test values ( 2 , '30/05/2009' );
1 row created.
SQL> select * from nls_date_test where date_entered = '2009-09-09';
select * from nls_date_test where date_entered = '2009-09-09'
*
ERROR at line 1:
ORA-01861: literal does not match format string
SQL> alter session set nls_date_format = 'YYYY-MM-DD';
Session altered.
SQL> select * from nls_date_test where date_entered = '2009-05-30';
ID DATE_ENTER
---------- ----------
2 2009-05-30
SQL> select value from v$nls_parameters where parameter = 'NLS_DATE_FORMAT';
VALUE
----------------------------------------------------------------
YYYY-MM-DD
SQL>

Related

Set NLS_DATE_FORMAT in oracle 11g to truncate time if it is not present

I have a SELECT query in PLSQL that contains multiple DATE columns and some of them also include TIME in it. The date and time must be displayed in standard format i.e; DD/MM/YYYY HH:MI:SS AM So before executing the query I set NLS_DATE_FORMAT parameter:
ALTER SESSION SET NLS_DATE_FORMAT = 'DD/MM/YYYY HH:MI:SS AM';
The problem here is that those columns which only have a DATE in it (without TIME) also shows a default time in result set i.e; 12:00:00 AM
Is there a way in oracle to setup NLS_DATE_FORMAT param in way that it truncates default time if it isn't present in DATE filed?
Note that I am aware of methods like TO_CHAR and TRUNC to achieve the desired results but in my case I can't use these because it will affect data sorting in my application.
There is no way to achieve this by NLS_SETTINGS. NLS_DATE_FORMAT specifies the default date format to use with the TO_CHAR and TO_DATE functions and applies to each and every single column. The default value of this parameter is determined by NLS_TERRITORY.
The value of this parameter can be any valid date format mask, and the value must be surrounded by double quotation marks.
Oracle always stores the time, but it will show it as long as the date format specified provides a date/time mask
Example
SQL> create table mytest ( c1 date ) ;
Table created.
SQL> insert into mytest values ( to_date ( '22/07/2020 11:25:00 AM' , 'DD/MM/YYYY HH:MI:SS AM' ) ) ;
1 row created.
SQL> insert into mytest values ( to_date ( '22/07/2020' , 'DD/MM/YYYY' ) ) ;
1 row created.
SQL> select * from mytest ;
C1
---------
22-JUL-20
22-JUL-20
SQL> select value from nls_database_parameters where parameter = 'NLS_DATE_FORMAT' ;
VALUE
--------------------------------------------------------------------------------
DD-MON-RR
SQL> alter session set nls_date_format = 'DD/MM/YYYY HH:MI:SS AM';
Session altered.
SQL> select * from mytest ;
C1
----------------------
22/07/2020 11:25:00 AM
22/07/2020 12:00:00 AM
SQL>
You have to apply some function ( TRUNC or TO_CHAR ) in order to achieve what you want. Oracle always store the time.
Oracle dates (as long as we're talking about actual dates and not just text) always have a time component.
I suspect the confusion arouses because you can feed DATE columns with texts that contain dates but not time. What really happens here is that ANSI date literals and the TO_DATE() function will just store a default value for missing components. In this case, midnight:
create table test (
id int not null,
value date not null,
primary key (id)
);
insert into test (id, value) values (1, date'2020-07-22');
insert into test (id, value) values (2, to_date('2020-07-22', 'YYYY-MM-DD'));
select id, value as default_format, to_char(value, 'YYYY-MM-DD HH24:MI:SS') as custom_format
from test;
(Demo)
If you really need it you can write a custom formatting function that checks if time is midnight and omits it, but of course there's just no way to tell whether the value was the default one or it's part of actual data explicitly entered that way.
This is easily obtained by specifying the the desired date format and NOT relying on nls_date_format. Further you can conditionally format the result yes sort date/time correctly.
with x (date_col) as
( select trunc(sysdate) from dual union all
select sysdate - 1 from dual
)
select
case when to_char(date_col, 'hh24:mi:ss') = '00:00:00'
then to_char(date_col, 'yyyy-mm-dd')
else to_char(date_col, 'yyyy-mm-dd hh:mi:ss am')
end date_col
from x
order by date_col;

How do I fix ORA-01843: not a valid month?

So at the query level, I have it:
to_char(
(
to_date(
substr(TIMESTAMP, 1, 19),
'yyyy-mm-dd hh24:mi:ss'
)
),
'dd-mon-yyyy hh24:mi:ss'
) as DateTime,
And I've tried looking at a few articles with one most notable:
How to change the date format in Oracle BI Publisher?
I have also tried using:
and trunc(TIMESTAMP) between :FROM_DATE AND :TO_DATE
--and also
and trunc(TIMESTAMP) between to_date(:FROM_DATE, 'yyyy-MM-dd') AND to_date(:TO_DATE, 'yyyy-MM-dd')
While going through structure and XML I noticed my date is in string format:
element name="DATETIME" value="DATETIME" label="DATETIME" dataType="xsd:string" breakOrder="ascending" fieldOrder="3"
So I removed the to_char to get the date format
The error I've been getting is:
java.sql.SQLDataException: ORA-01843: not a valid month
How do I fix this issue?
EDIT:
Format for the column, TIMESTAMP, the format is CHAR(14)
Example of values is like 20200701103038
It runs perfectly in SQL Developer
Well, it is quite a bad and extended practice to store DATES as strings, either using varchar2 or char. Anyway, having say that, I think you have a problem with your settings or the way you are constructing your query:
SQL> alter session set nls_date_format='YYYYMMDDHH24MISS' ;
Session altered.
SQL> select to_date('20200726123722') from dual ;
TO_DATE('20200
--------------
20200726123722
SQL> select sysdate from dual ;
SYSDATE
--------------
20200726124622
Besides, as you said, if your data is stored as YYYYMMDDHHMISS, you are applying the wrong date mask YYYY-MM-DD HH24:MI:SS to that char. I would use CAST to define the field as DATE.
Example
SQL> create table my_test ( c1 char(20) ) ;
Table created.
SQL> insert into my_test values ('20200726123722') ;
1 row created.
SQL> insert into my_test values ('20200725123722') ;
1 row created.
SQL> commit ;
Commit complete.
SQL> alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss';
Session altered.
SQL> select cast(c1 as date) from my_test ;
CAST(C1ASDATE)
-------------------
2020-07-26 12:37:22
2020-07-25 12:37:22
SQL>
Update
If you can't change your NLS Session settings, then you must apply a TO_CHAR to the resulting output. But in your case, you want to operate with dates, so as long as it is a date value you want to operate with, you can forget about the mask.
SQL> col value for a20
SQL> select value from nls_database_parameters where parameter = 'NLS_DATE_FORMAT' ;
VALUE
--------------------
DD-MON-RR
SQL> select cast(to_date('20200725123722','YYYYMMDDHH24MISS') as date) from dual ;
CAST(TO_D
---------
25-JUL-20
SQL> select to_char( cast(to_date('20200725123722','YYYYMMDDHH24MISS') as date) , 'YYYYMMDDHHMISS' ) from dual ;
TO_CHAR(CAST(T
--------------
20200725123722
SQL> select case when cast(to_date('20200725123722','YYYYMMDDHH24MISS') as date) > sysdate
2 then 'FALSE'
3 else
4 'TRUE'
5 end as result from dual ;
RESUL
-----
TRUE
SQL>
So, if you want to compare the date to another date, don't use to_char. If you want to show the value in a specific format, when you have no option to change the settings, then use to_char.
Just to make sure what SYSDATE (I'm going to select) represents:
SQL> alter session set nls_Date_format = 'dd.mm.yyyy';
Session altered.
Today is:
SQL> select sysdate from dual;
SYSDATE
----------
26.07.2020
This is the way to get the error you got: apply wrong format mask to a string which represents a DATE value:
SQL> select to_Date('2020-27-07', 'yyyy-mm-dd') from dual;
select to_Date('2020-27-07', 'yyyy-mm-dd') from dual
*
ERROR at line 1:
ORA-01843: not a valid month
SQL>
How to fix it? Usually, it is hard to fix it if dates are represented as strings. They (strings that represent dates) are like a box of chocolates, you never know what you're gonna get. If there's at least one wrong value, query will fail.
How to find wrong values? You could create a function which returns TRUE (or 1 or whatever you want) if a string you pass to it represents a valid date format. But, if you pass 01/02/03, which is which? Different formats match (e.g. dd/mm/yy, yy/mm/dd, mm/yy/dd ...). Worse cases are 84/25/32 or AB/23/2f. They are all strings, they "match" two characters separated by slash but certainly aren't valid dates, so you can't rely on a simple regular expression.
Shortly, there's no easy nor fast way out of it.

Inserting a TIMESTAMP value in SQL Developer

Hello I'm having a trouble inserting this value in Oracle SQL Developer Version 19.4.0.354.
'2013-01-01 00:00:00.0000'
Here is the value that I want to insert in one of my tables.
I tried DATE and TIMESTAMP data types but they don't work. I also tried altering the sessions and other possible solutions all over the internet.
Column datatype should be TIMESTAMP. Use appropriate format mask in TO_TIMESTAMP function.
SQL> create table test (col timestamp);
Table created.
SQL> insert into test (col) values (to_timestamp('2013-01-01 00:00:00.0000', 'yyyy-mm-dd hh24:mi:ss:ff6'));
1 row created.
What's in there? (alter session is here just to display the result in desired format; it doesn't affect the value stored in that column):
SQL> alter session set nls_timestamp_format = 'dd.mm.yyyy hh24:mi:ss.ff6';
Session altered.
SQL> select * From test;
COL
---------------------------------------------------------------------------
01.01.2013 00:00:00.000000
SQL>
The simplest approach is to use a literal timestamp, so you don't rely on implicit conversion:
select timestamp '2013-01-01 00:00:00.0000' from dual;
You can also use to_timestamp():
select to_timestamp('2013-01-01 00:00:00.0000', 'yyyy-mm-dd hh24:mi:ss:ff4') from dual;
On the other hand, if you really want to work with a string, then you would need to change the nls setting to your own format before:
alter session set nls_timestamp_format = 'yyyy-mm-dd hh24:mi:ss:ff4';
You can then use pass your string value directly in the query, and Oracle will implicitly convert it to the target datatype while inserting.
You have no time component, so I would just use:
date '2013-01-01'
This is the safest syntax (and clearest and standard too!) for providing a date value.
If Oracle wants a timestamp, it will convert this correctly.

How to get time string to Time in oracle sql

I have insert time in form of string in oracle VARCHAR2 column. But when I try to retrieve it in form of time it's not giving me right time, it's only giving me a date which I have not saved.
INSERT INTO table1
(timestr) Select substr(numtodsinterval(MAX(date1)-MIN(date2),'day'),
12,8) from table2 where ....; // stored timestr column value: 00:00:00
Retrieve ...
select TO_DATE(timestr,'hh24:mi:ss') from table1;
... is only giving 10/01/2015
You should use to_char to see the time
select to_char(timestr,'hh24:mi:ss') from table1;
That might be because of your client setting. If you are using SQL Developer, then go to Tools->Preference->Database->NLS and change setting to view timestamp also.
If you are using SQLPlus, the change nls_date_format or see below solution.
https://community.oracle.com/thread/312115?start=0&tstart=0
In Oracle there is no concept of a TIME datatype. There are only DATE and TIMESTAMP, both of which comprise date and time elements.
Your retrieval casts your time column to a DATE: TO_DATE(timestr,'hh24:mi:ss'). Oracle does what we ask it to do, so it displays a date. As there is no date element in timestr it assigns one for us: today's date.
You are only seeing 10/01/2015 because your client's NLS settings are configured to display only the date element. Change the setting to show the time element, in the client or by altering the session as here:
SQL> select to_date('10:08:23', 'hh24:mi:ss') ts from dual
2 /
TS
---------
01-OCT-15
SQL> alter session set nls_date_format='DD-MON-YYYY HH24:MI:SS'
2 /
Session altered.
SQL> select to_date('10:08:23', 'hh24:mi:ss') ts from dual
2 /
TS
--------------------
01-OCT-2015 10:08:23
SQL>
If you only want to see the time you can change the NLS settings ...
SQL> alter session set nls_date_format='HH24:MI:SS'
2 /
Session altered.
SQL> select to_date('10:08:23', 'hh24:mi:ss') ts from dual
2 /
TS
--------
10:08:23
SQL>
...but that will get annoying ....
SQL> select sysdate from dual
2 /
SYSDATE
--------
23:59:52
SQL>
So why not just display the column as a string all without casting to a DATE?
select timestr from table1;

Number formatting in SQL

I have a number that needs to be formatted like this:
Thousands need to be separated with .
Decimals need to be separated with ,
For example, number 1,234,567.89 needs to look like 1.234.567,89.
Is there any way that I can do this with a simple sql function or I have to make my own function?
Use to_char() together with the specification that you want to use , as the decimal separator and . for the thousands separator (which is not the default in Oracle)
select to_char(1234567.89, '9G999G999G999D00', 'NLS_NUMERIC_CHARACTERS = '',.''')
from dual;
Results in: 1.234.567,89
Details about format models: http://docs.oracle.com/cd/E11882_01/server.112/e41084/sql_elements004.htm#SQLRF00211
Details about the to_char() function: http://docs.oracle.com/cd/E11882_01/server.112/e41084/functions201.htm#SQLRF51882
You can alternatively also work with the session territory setting.
create table mytest (field1 number);
insert into mytest values (1234567.89);
alter session set NLS_TERRITORY=GERMANY;
select field1, to_char(field1,'9G999G999G999D00') from mytest;
alter session set NLS_TERRITORY=AMERICA;
select field1, to_char(field1,'9G999G999G999D00') from mytest;
Output:
Table created.
1 row created.
Session altered.
FIELD1 TO_CHAR(FIELD1,'9G999G999G999D00')
---------- ----------------------------------
1234567,89 1.234.567,89
1 row selected.
Session altered.
FIELD1 TO_CHAR(FIELD1,'9G999G999G999D00')
---------- ----------------------------------
1234567.89 1,234,567.89
1 row selected.