Select throws an ORA-01858 exception - sql

With the following query, the Oracle exception is thrown.
However, I cant see why. Can anyone shed some light?
select visit_id, to_date(response, 'DD/MM/YYYY') as convertedDate from
(
select *
from dat_results_ext
where item_name = 'CALLBACKDATE'
)
where to_date(response, 'DD/MM/YYYY') > sysdate
I understand the exception to be mean that its trying to convert the 'response' field, but it is meeting a non-numeric. Problem is the row that it should bring back has everything in the right format.
The 'response' field is a varchar field, but all the rows coming back with the 'item_name = 'CALLBACKDATE' clause are all of the correct format.
Any ideas?

The optimizer can rewrite your query before trying to find the best execution plan. In your case since you have no hints that would prevent the optimizer from doing this, it will probably unnest your subquery and rewrite your query as:
SELECT *
FROM dat_results_ext
WHERE item_name = 'CALLBACKDATE'
AND to_date(response, 'DD/MM/YYYY') > sysdate
You don't have control over the order of evaluation of the statements in the WHERE clause, so Oracle probably evaluated the to_date function first on a row that is not convertible to a date, hence the error.
I see two options to force Oracle to evaluate the statements in the order you want:
Use rownum. Rownum will materialize the subquery, preventing Oracle from merging it with the outer query:
SELECT visit_id, to_date(response, 'DD/MM/YYYY') AS convertedDate
FROM (SELECT r.*,
rownum /* will materialize the subquery */
FROM dat_results_ext r
WHERE item_name = 'CALLBACKDATE')
WHERE to_date(response, 'DD/MM/YYYY') > sysdate
Use the NO_MERGE hint:
SELECT visit_id, to_date(response, 'DD/MM/YYYY') AS convertedDate
FROM (SELECT /*+ NO_MERGE */ *
FROM dat_results_ext
WHERE item_name = 'CALLBACKDATE')
WHERE to_date(response, 'DD/MM/YYYY') > sysdate

The TO_DATE clause has to be evaluated before the truth of the WHERE clause can be determined. If you have values of response that can't be evaluated in the TO_DATE function, you'll see the error.

To be very precise, this is caused because some value of response do not match the format mask of DD/MM/YYYY. For example, if your session is set to default date format of DD-MON-YY, execute the following and you will receive the error message:-
select to_date('17/SEP/2012','DD/MM/YYYY') from dual;
ERROR:
ORA-01858: a non-numeric character was found where a numeric was expected
Since you passed a character in the month field and Oracle expects a number.

Related

query to subtract date from systimestamp in oracle 11g

I want to perform a subtraction operation on the date returned from another query and the system time in oracle SQL. So far I have been able to use the result of another query but when I try to subtract from systimestamp it gives me the following error
ORA-01722: invalid number
'01722. 00000 - "invalid number"
*Cause: The specified number was invalid.
*Action: Specify a valid number.
Below is my query
select round(to_number(systimestamp - e.last_time) * 24) as lag
from (
select ATTR_VALUE as last_time
from CONFIG
where ATTR_NAME='last_time'
and PROCESS_TYPE='new'
) e;
I have also tried this
select to_char(sys_extract_utc(systimestamp)-e.last_time,'YYYY-MM-DD HH24:MI:SS') as lag
from (
select ATTR_VALUE as last_time
from CONFIG
where ATTR_NAME='last_time'
and PROCESS_TYPE='new'
) e;
I want the difference between the time intervals to be in hours.
Thank you for any help in advance.
P.S. The datatype of ATTR_VALUE is VARCHAR2(150). A sample result of e.last_time is 2016-09-05 22:43:81796
"its VARCHAR2(150). That means I need to convert that to date"
ATTR_VALUE is a string so yes you need to convert it to the correct type before attempting to compare it with another datatype. Given your sample data the correct type would be timestamp, in which case your subquery should be:
(
select to_timestamp(ATTR_VALUE, 'yyyy-mm-dd hh24:mi:ss.ff5') as last_time
from CONFIG
where ATTR_NAME='last_time'
and PROCESS_TYPE='new'
)
The assumption is that your sample is representative of all the values in your CONFIG table for the given keys. If you have values in different formats your query will break on some other way: that's the danger of using this approach.
So finally after lots of trial and errors I got this one
1. Turns out initially the error was because the data_type of e.last_time was VARCHAR(150).
To find out the datatype of a given column in the table I used
desc <table_name>
which in my case was desc CONFIG
2. To convert VARCHAR to system time I have two options to_timestamp and to_date. If I use to_timestamp like
select round((systimestamp - to_timestamp(e.last_time,'YYYY-MM-DD HH24:MI:SSSSS')) * 24, 2) as lag
from (
select ATTR_VALUE as last_time
from CONFIG
where ATTR_NAME='last_time'
and PROCESS_TYPE='new'
) e;
I get an error that round expects NUMBER and got INTERVAL DAY TO SECONDS since the difference in date comes out to be like +41 13:55:20.663990. To convert that into hour would require a complex logic.
An alternative is to use to_data which I preferred and used it as
select round((sysdate - to_date(e.last_time,'YYYY-MM-DD HH24:MI:SSSSS')) * 24, 2) as lag
from (
select ATTR_VALUE as last_time
from CONFIG
where ATTR_NAME='last_time'
and PROCESS_TYPE='new'
) e;
This returns me the desired result i.e. the difference in hours rounded off to 2 floating digits

Start a Case Statement without an outside 'Select' - CTE's

Running this query (below) returns a 'Too Many Values' error:
select
case
when to_char(sysdate, 'yyyymmdd') = to_char(sysdate, 'yyyymm') || '01'
then (select FirstReportGroups.*, FirstReportDetails.*
from FirstReportGroups, FirstReportDetails)
when to_char(sysdate, 'yyyymmdd') = to_char(sysdate, 'yyyymm') || '16'
then (select SecondReportGroups.*, SecondReportDetails.*
from SecondReportGroups, SecondReportDetails)
end as LetsSee
from cmtmpentered t1 join cmtmpconf t2
on t1.casenumber = t2.casenumber
and t1.enc = t2.enc
;
I'm using CTE's (they are not included because it would make this very long) and it makes logical sense to me, but googling this 'Too Many Values' error gives me no substantial help. Running the CTE's individually works, so that is not the problem.
I think all would be solved if I could only get rid of the outside 'Select' statement and just keep the selects inside the Case. If I'm explaining this poorly, an example of what I'm looking for is this:
case
when to_char(sysdate, 'yyyymmdd') = to_char(sysdate, 'yyyymm') || '01'
then (select FirstReportGroups.*, FirstReportDetails.*
from FirstReportGroups, FirstReportDetails)
when to_char(sysdate, 'yyyymmdd') = to_char(sysdate, 'yyyymm') || '16'
then (select SecondReportGroups.*, SecondReportDetails.*
from SecondReportGroups, SecondReportDetails)
end as LetsSee
;
Is this doable in any capacity? This syntax obviously doesn't work, otherwise I wouldn't be asking the question - but is there a related way I can do this?
select FirstReportGroups.*, FirstReportDetails.*
from (select 1 a from dual) FirstReportGroups
cross join (select 2 b from dual) FirstReportDetails
where extract(day from sysdate) = 1
---------
union all
---------
select SecondReportGroups.*, SecondReportDetails.*
from (select 3 a from dual) SecondReportGroups
cross join (select 4 b from dual) SecondReportDetails
where extract(day from sysdate) = 16;
Replaced common table expressions with inline views. CTEs should only be used if they are referenced more than once. They may look a little nicer with small examples and for programmers only used to procedural code. Serious SQL requires multiple nested levels of inline views. And debugging is much easier without CTEs - the CTEs make it difficult to highlight and run sub-blocks of code.
Replaced case expression with predicate to filter by date. A CASE expression can only return a single value. You could probably do something fancy with types, but that would be horribly complicated. My code still makes the assumption that the two sets return the same types of values. If that's not true you'll need to do something different at the application level.
Replaced to_char with extract. Date handling can be confusing in Oracle. The trick is to keep everything in their native type. You should almost never need to use to_char or to_date for anything other than formatting for display. For any other operation there's almost certainly a function to handle it without converting types.
Replaced , with ANSI-syntax cross join. The ANSI-style syntax has several advantages. One major advantage is it makes it clear when cross joins are intentional.
You aren't specifying a join condition for LetsSee. You are effectively cross joining LetsSee with the result of your from clause:
from cmtmpentered t1
join cmtmpconf t2 on t1.casenumber = t2.casenumber and t1.enc = t2.enc
I suggest you join LetsSee with t1 or t2.
Also, you are not specifying a join condition for the pairs:
FirstReportGroups and FirstReportDetails
SecondReportGroups and SecondReportDetails

PLSQL 'IN' Operator

I'm trying to select rows based on a range of 'dates' determined by the following PLSQL query, which currently delivers the results I need - being the 'date' object of the last 10 weeks of the day of the week when the script is run. Eg. running it on the 22th of May would yield, 15th May, 8th May and so on.
SELECT SYSDATE-(level*7) as DateRange
FROM DUAL
CONNECT BY LEVEL <=10
This generates a list of dates. Then I try and combine this with a parent select statement to get rows with the dates outputted by the above that are in the 'DAY' (of Oracle type DATE) column.
SELECT * FROM NEM_RM16
WHERE NEM_RM16.DAY IN (
SELECT SYSDATE-(level*7) as DateRange
FROM DUAL
CONNECT BY LEVEL <=10);
Which gives no results, despite knowing that there are rows that have the dates generated by the above.
I've read that when using the 'IN' operator, values must be enclosed in single quotes, but I'm not sure about how to do this with the query above.
Am I going about this the right way by using the IN operator, or should I be doing a different type of nested query?
use trunc for truncate time component from sysdate
SELECT * FROM NEM_RM16
WHERE NEM_RM16.DAY IN (
SELECT trunc(SYSDATE)-(level*7) as DateRange
FROM DUAL
CONNECT BY LEVEL <=10);
Maybe the format of the date returned by nested query does not match with the date format of the column NEM_RM16.DAY
Probably, if the dates are compared after making them of the same format, they will match properly
Like this
SELECT *
FROM NEM_RM16
WHERE TO_DATE(TO_CHAR(NEM_RM16.DAY, 'DD/MM/YYYY'), 'DD/MM/YYYY') IN
(SELECT TO_DATE(TO_CHAR(SYSDATE - (level * 7), 'DD/MM/YYYY'),
'DD/MM/YYYY') as DateRange
FROM DUAL
CONNECT BY LEVEL <= 10);
Hope it helps

Using a variable in an SQL query (Oracle DBMS)?

I am typing SQL queries to get some data from a software tool which is based on an Oracle database. I am using the typical SELECT-statement.
Now, in my SQL-query I am using at different places the date "02.05.2012". Is there a way to define a variable date_string at the beginning and then use at all relevant places this variable?
This would simplify things a lot. Thanks for all hints and tips!
You might try to rewrite your query to return the literal from an inline view ...
select
my_date,
...
from(
select to_date('02.05.2012','DD.MM.YYYY') my_date from dual),
table2
where
some_column <= my_date
What you look for is a bind variable.
select to-date(:date, 'dd.mm.yyyy') date1
, to-date(:date, 'dd.mm.yyyy') + 1 date2
from dual
On runtime you need to pass the value to the bind variable. It all depends on your programming language how to bind the variable, but there is plenty documentation for that.
DEFINE only works if you use sql*plus, and that's usually not the case inside a "software tool" :)
EDIT:
I'm beginning to understand now. It's just a textarea where you can enter a query and it will execute it and return the result. In that case you either write some complicated pl/sql code, or enter all the dates manually, or use a cross join with a select from dual:
with (select to_date('02.05.2012', 'dd.mm.yyyy') my_date from dual) d
select *
from some_table t
cross join d -- no ON required
If you want to select using the current date you can use sysdate.
Using SQLPLUS you can define your own variables:
SQL> define mydate ="01-05-2012"
SQL> select to_date('&mydate','DD-MM-YYYY') from dual;
01-MAY-12
try the following :
SELECT *
FROM table1
WHERE to_char(date1,'DD/MM/YYYY') = '&&date'
AND to_char(date2,'DD/MM/YYYY') = '&&date'
AND to_char(date3,'DD/MM/YYYY') = '&&date'
you will get a prompt to enter the value for the &&date , if you want to enter different values for each date, you should type &date instead of &&date
DEFINE is useful for your requirement.
DEFINE NAME="example"
access with &NAME

Can NVL Function be Cascaded?

can nvl() function be cascaded,...it was asked me in IBM interview .....and why????
Better yet, use COALESCE
My answer:
Yes, NVL can be cascaded. It's something I'd expect to see on code ported from Oracle versions up to and including 8i, because COALESCE wasn't supported until Oracle 9i.
COALESCE is an ANSI SQL 99 standard, and works like a CASE/SWITCH statement in how it evaluates each expression in order and does not continue evaluating expressions once it hits a non-null expression. This is called short-circuiting. Another benefit of using COALESCE is that the data types do not need to match, which is required when using NVL.
This:
SELECT COALESCE( 1.5 / NULL,
SUM(NULL),
TO_CHAR(SYSDATE, 'YYYY') ) abc
FROM DUAL
...will return: 2009 (for the next ~32 days, anyways)
Why not? For example:
select NVL( null, NVL( null, 1 )) from dual
It can be something like:
select NVL( delete_date, NVL( edit_date, create_date )) AS last_change
from Table
May be they wanted you to say that it is deterministic function. So it is reentrant.
The most common reason that I have seen for cascaded NVL's is that databases change over time.
The original design had a table that later was changed to have more columns in the table.
Alter statements created the new columns as NULL allowed and this was taken advantage of with view so that the
code on top did not need to change. The problem was that a single column, change_date,
was replaced with multiple columns. Update_Date, Comment_Date, and Approval_date. Each of the
3 new columns is now combined to get a "change_date" in the view with
create or replace view OldTableNmae as
select other_columns
, nvl ( update_date, nvl ( comment_date, approval_date ) ) change_date
, more_columns
from new_table
/
As others said, NVL can be cascaded, but the preferred solution would be to use COALESCE instead. However, the two functions are not entirely interchangeable:
1) as mentioned elsewhere, COALESCE only evaluates the arguments from the first until it meets one that does not evaluate to null
2) however, COALESCE requires all arguments to be of the same data type, thus being STRICTER than NVL, which will first attempt an implicit conversion.
E.g.
SELECT COALESCE( 1.5 / NULL,
SUM(NULL),
TO_CHAR(SYSDATE, 'YYYY') ) abc
FROM DUAL
throws in fact the error ORA-00932: inconsistent datatypes: expected NUMBER got CHAR
NVL would instead return the current year
SELECT NVL( 1.5 / NULL,
NVL( SUM(NULL),
TO_CHAR(SYSDATE, 'YYYY') ) ) abc
FROM DUAL
Other examples:
select coalesce('some text',sysdate) from dual;
throws ORA-00932: inconsistent datatypes: expected CHAR got DATE, while
select nvl('some text',sysdate) from dual;
returns some text, but
select nvl(sysdate,'some text') from dual;
throws ORA-01841: (full) year must be between -4713 and +9999, and not be 0 (because the implicit conversion attempt of 'some text' to a date has failed)