Is it possible to convert a varchar2 column to a user defined type? - sql

I have a type declared as
TYPE "TABLE_TYPE" AS TABLE OF varchar2(4000)
How can i convert a column i am selecting in a query to return that type.
For example
Select cast(to_char(sysdate) as TABLE_TYPE) from dual
Returns an ORA-00932: inconsistent datatypes: expected - got CHAR error. Is there a way to convert a column which is a varchar2 to be of the user defined type?

If you really want to return a collection with a single element, you would just need to call the type's constructor
SQL> create type table_type as table of varchar2(100);
2 /
Type created.
SQL> select table_type( to_char( sysdate ))
2 from dual;
TABLE_TYPE(TO_CHAR(SYSDATE))
-----------------------------------------------------------------------------
TABLE_TYPE('14-FEB-13')
Normally, though, when you are creating a collection, the intent is to populate it with multiple elements by running a query that returns multiple rows. For that, you would want to do something like
DECLARE
l_strs table_type;
BEGIN
SELECT ename
BULK COLLECT INTO l_strs
FROM emp;
<<l_strs now contains 1 element for each row in the EMP table>>
END;

Use cast + multiset
select cast(multiset(
select to_char(sysdate-level, 'YYYYMMDD')
from dual
connect by level <= 10
) as t_vchar_tab)
from dual;

Union causing an ORA-01790: expression must have same datatype as corresponding expression
CREATE OR REPLACE TYPE varchar2_ntt AS TABLE OF VARCHAR2(4000);
/
SELECT deptno
, CAST(COLLECT(ename) AS varchar2_ntt) AS emps
FROM scott.emp
GROUP BY deptno
/

select table_type(to_char(sysdate)) from dual;

Related

ORA-01460 unimplemented or unreasonable conversion requested

I am passing ID's in oracle proc. ID's can be in 1000's. currently its able to process around 600 ID's, if I pass more than 600 ID's - I am getting ORA-01460 unimplemented or unreasonable conversion requested. ID is varchar2 datatype, how can I process 1000's of Id's in varchar2 or what will be the best strategy to handle this kind of issue. Any guidance/suggestion will be highly appreciated. Can this be solved using CLOB datatype?
//this is how I am processing Id's
create or replace procedure Emp(
emp_id in varchar2
)
//passing those id's in CTE before passing to subquery
WITH
EMP_LIST AS(
select regexp_substr(emp_id,'[^,]+', 1, level) from dual
connect by level <= LENGTH(regexp_substr(emp_id, '[^,]+'))+1
)
Pass a collection or VARRAY rather than passing a comma-delimited string:
CREATE TYPE number_list IS TABLE OF NUMBER(10,0);
Then you can use it something like:
CREATE PROCEDURE emp(
emp_ids IN number_list
)
IS
BEGIN
-- Do something with the ids like inserting them into a table
INSERT INTO employees ( id )
SELECT COLUMN_VALUE
FROM TABLE( emp_ids );
-- Or something like this:
SELECT something
INTO some_variable -- you need to define this variable first
FROM some_table
WHERE emp_id MEMBER OF emp_ids;
END;
/
Update
If you can't create anything then you can use a built-in collection like SYS.ODCINUMBERLIST:
CREATE PROCEDURE emp(
emp_ids IN SYS.ODCINUMBERLIST
)
IS
BEGIN
-- Do something with the ids like inserting them into a table
INSERT INTO employees ( id )
SELECT COLUMN_VALUE
FROM TABLE( emp_ids );
-- Or something like this:
SELECT something
INTO some_variable -- you need to define this variable first
FROM some_table
WHERE emp_id IN ( SELECT COLUMN_VALUE FROM TABLE( emp_ids ) );
END;
/
(Note: SYS.ODCI*LIST types are VARRAY data types and do not support the MEMBER OF operator like collections do; instead you can get the values from the VARRAY using a nested SELECT statement with a TABLE() collection expression.)
However, if you really can't CREATE anything then you won't be able to CREATE PROCEDURE .... not sure there is any solution to that apart from talking to your DBA.

Why isn't Oracle converting characters to numbers?

Oracle throws ORA-01722: invalid number in my SQL query and it is unclear why.
I have a table called "LIGHTS" and I want to get the lights with a WATTAGE <= 3. WATTAGE is stored as a VARCHAR2(40) for some reason, but each character does seem to be an integer or float. When I convert WATTAGE to a number using the query:
SELECT TO_NUMBER(WATTAGE) FROM LIGHTS
There's no problem. I get a result like this:
TO_NUMBER(WATTAGE)
1
7
-1
0
15
17.5
However, when I add a WHERE condition to filter the numbers for those less than 3, I get the ORA-01722: invalid number error:
SELECT WATTAGE FROM LIGHTS
WHERE TO_NUMBER(WATTAGE) <= 3
What could be going wrong?
ORA-01722: invalid number comes from the TO_NUMBER(), not from the conditional. I.e., try this and you'll get the same error:
SELECT TO_NUMBER('test') FROM dual;
This would indicate that at least one of your values is not numeric.
Alas Oracle doesn't have a simple way to check whether a string is in fact representing a number. (One of the many reasons to use the correct data type in the first place!)
However, you can write your own. Here is just a brief demo of this concept. I create a table with a column of VARCHAR2 data type, and populate it with a few strings, one of which is not a number.
create table tbl (nbr varchar2(100));
insert into tbl
select '103' from dual union all
select '-1.3' from dual union all
select 'abc' from dual
;
Then I create a small function with a nested block that should error out if TO_NUMBER fails. The error handler will "do something" specific to errors and then return control to the main function. Then I can use this in a WHERE clause. Here are the function and then how it can be used to find the offending values:
create or replace function not_a_number(str varchar2)
return varchar2
as
x number;
r varchar2(100);
begin
begin
x := to_number(str);
exception
when others then
r := str;
end;
return r;
end;
/
select nbr
from tbl
where not_a_number(nbr) is not null;
NBR
-------
abc

How to use SELECT request in stored procedure (Oracle)?

I use Oracle 12c. I know that my next question is not new but I am little bit confused and need help.
I have this SQL statement:
SELECT *
FROM TABLE_NAME
WHERE CREATE_DATE BETWEEN TO_DATE(FIRST_DATE, 'YYYY-MM-DD')
AND TO_DATE(SECOND_DATE , 'YYYY-MM-DD')
Questions:
How correctly to use SELECT request in stored procedure?
That SQL statement returns more than 1 row, is it mean that I need to use cursor?
If table has 15 columns, as output I need to set all of them?
EDIT:
CREATE OR REPLACE PROCEDURE PROCEDURE_NAME
(
FIRST_DATE IN VARCHAR2(10),
SECOND_DATE IN VARCHAR2(10)
)
AS
oracle_cursor SYS_REFCURSOR;
BEGIN
OPEN oracle_cursor FOR
SELECT *
FROM scheme_name.table_name
WHERE CREATE_DATE BETWEEN TO_DATE(FIRST_DATE, 'YYYY-MM-DD') AND TO_DATE(SECOND_DATE, 'YYYY-MM-DD');
DBMS_SQL.RETURN_RESULT(oracle_cursor);
END PROCEDURE_NAME;
How correctly to use SELECT request in stored procedure?
In a stored procedure you need to assign the queried result set to a variable (or variables) which match the projection:
select << column >>, << column >>
into << variable >>, << variable >>
from table_name
....
That SQL statement returns more than 1 row, is it mean that I need to use cursor?
A cursor is one way of handling it. Although a cursor loop is usually the better approach:
for r in ( SELECT *
FROM TABLE_NAME
WHERE CREATE_DATE BETWEEN TO_DATE(FIRST_DATE, 'YYYY-MM-DD')
AND TO_DATE(SECOND_DATE , 'YYYY-MM-DD')
) loop
Populating a collection variable is another approach, using the BULK COLLECT:
select << column >>
bulk collect into << collection >>
from table_name
....
If table has 15 columns, as output I need to set all of them?
You can choose to create fifteen distinct variables. However, if your query's projection matches the table's projection (which it does with select *) you can use the %rowtype construct to define a record variable or a collection:
declare
l_rec TABLE_NAME%rowtype; -- single row
type t_rec is table of TABLE_NAME%rowtype; -- multiple rows
l_recs t_rec
begin
SELECT *
bulk collect into l_recs
FROM TABLE_NAME
WHERE CREATE_DATE BETWEEN TO_DATE(FIRST_DATE, 'YYYY-MM-DD')
AND TO_DATE(SECOND_DATE , 'YYYY-MM-DD');
I need to take the result and show that data in web page.
For returning results you probably just need to return a Ref Cursor. This is just a pointer which maps to constructs like JDBC ResultSet and ODBC ResultSet. For PHP this would be an oci_new_cursor. Find out more.
Create or replace procedure get_recs
(FIRST_DATE in varchar2,
SECOND_DATE in varchar2,
OUT_RECS out sys_refcursor
) is
Begin
Open out_recs for
SELECT *
FROM TABLE_NAME
WHERE CREATE_DATE BETWEEN TO_DATE(FIRST_DATE, 'YYYY-MM-DD')
AND TO_DATE(SECOND_DATE , 'YYYY-MM-DD');
End;
Incidentally, you seem to expect to pass the parameters as strings: it would be better to pass them as actual dates.

How to store query result in a variable in sql

I am using following query:
DECLARE
result varchar2(100);
BEGIN
select (systimestamp - (select date_time from test where id=2945134)) into result from dual;
SELECT SUBSTR(result, 3,1) Final_result
FROM DUAL;
END;
It is showing an error like:
PLS-00428: an INTO clause is expected in this SELECT statement
06550. 00000 - "line %s, column %s:\n%s"
Cause: Usually a PL/SQL compilation error.
I need to store result value from first query into result variable and then use that result variable to show the substring (3,1) as a Final_result.
DECLARE
result varchar2(100);
BEGIN
select substr((systimestamp - (select date_time from test where id=2945134)), 3,1)
into result
from dual;
dbms_output.put_line(result);
END;
/
But this is a highly dubious thing you are doing. You are relying on implicit datatype conversion (interval --> varchar) which will not work properly if your NLS settings change.
You would better use to_char() to format the resulting interval to something appropriate.
Instead of making 2 select from the same table, just 1 should suffice.
Also as a practice, always try using the fully qualified name of your objects
Why do you need the "DUAL" table
DECLARE
#final_result varchar2(100);
BEGIN
select #final_result= SUBSTR((systimestamp - (select date_time from test where id=2945134)),3,1);
END;

SQL Converting a varchar to a clob within an SQL union

Why does oracle not allow the following query
select to_clob(1) from dual
union
select wm_concat(sysdate) from dual;
wm_concat returns a clob. To make both queries in the union have the same type columns i convert the column in the first query to a clob but Oracle gives me an [1]: ORA-00932: inconsistent datatypes: expected - got CLOB error even though both are returning a clob value.
Each of the queries work individually and both return a clob value.
I don't believe wm_concat returns a CLOB.
This shows that the return is Typ=1 which is a VARCHAR2
SQL> select dump(wm_concat(sysdate)) from dual;
DUMP(WM_CONCAT(SYSDATE))
--------------------------------------------------------------------------------
Typ=1 Len=9: 49,52,45,70,69,66,45,49,51
which you can also see if you create a view
SQL> ed
Wrote file afiedt.buf
1 create view vw_wm_concat
2 as
3* select wm_concat(sysdate) col from dual
SQL> /
View created.
SQL> desc vw_wm_concat;
Name Null? Type
----------------------------------------- -------- ----------------------------
COL VARCHAR2(4000)
If you convert the VARCHAR2 returned by WM_CONCAT into a CLOB, the next problem is that Oracle doesn't support doing a DISTINCT on a CLOB column which is required in order to do a UNION. Assuming that you don't really need to remove duplicate rows, you can use a UNION ALL rather than a UNION.
Putting the two together, something like this
SQL> ed
Wrote file afiedt.buf
1 select to_clob(1) from dual
2 union all
3* select to_clob(wm_concat(sysdate)) col from dual
SQL> /
TO_CLOB(1)
------------------------------------------------------------
1
14-FEB-13
will work