SQL Converting a varchar to a clob within an SQL union - sql

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

Related

REGEXP_REPLACE expression issue

I got some sql script below to remove word between words, but it will find last occurrence of my (/hide) instead of the first occurrence. Need help to get the output as expected. Thanks.
select regexp_replace('(hide)it(/hide)should be show(hide)my(/hide) text',
'^\(hide\).*\(/hide\)', '') "TESTING"
from dual;
I expect the output will be:
should be show text
but the actual output is:
text
If my data is in one of the column with datatype of clob. As currently i use below script to select. For example my table is testing_table with column of desc_str with data_type of clob which inside contain value '(hide)it(/hide)should be show(hide)my(/hide) text';
select trim(to_char(regexp_replace(desc_str,'^\(hide\).*\(/hide\)',''))) as desc
from testing_table
where OOE_FP_SS_ID = $id;
Based on your example:
SQL> with test (col) as
2 (select '(hide)it(/hide)should be show(hide)my(/hide) text' from dual)
3 select regexp_replace(col, '\(hide\)\w+\(/hide\)', '') result
4 from test;
RESULT
-------------------
should be show text
SQL>
[EDIT: CLOB]
You asked for a CLOB. Here's an example:
SQL> create table test (desc_str clob);
Table created.
SQL> insert into test values ('(hide)it(/hide)should be show(hide)my(/hide) text');
1 row created.
SQL> select regexp_replace(desc_str, '\(hide\)\w+\(/hide\)', '') result from test;
RESULT
--------------------------------------------------------------------------------
should be show text
SQL>
Another example:
SQL> with test (desc_str) as
2 (select '(hide)ItemId: 4334(/hide)|(hide)Description Item - SubType:(/hide) Name - Item|(irow)Price Value: MYR 1,668' from dual)
3 select regexp_replace(desc_str, '\(hide\).+?\(/hide\)', '') result from test;
^
RESULT | this question mark was missing
------------------------------------------
| Name - Item|(irow)Price Value: MYR 1,668
SQL>

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

Convert LONG into VARCHAR2 or some text datatype

As we all know LONG is deprecated in Oracle a long back but Oracle itself is still using this datatype in their views.
So if I have to change LONG into some kind of text datatype how can I achieve that.
I am trying to query this and getting error.
ORA-00932: inconsistent datatypes: expected - got LONG
Query -
SELECT NVL(ie.column_expression, ic.column_name)
from user_ind_columns ic left join user_ind_expressions ie on ic.index_name = ie.index_name and ic.table_name = ie.table_name
where ic.table_name = 'Some Table'
There are several methods, one such is create table using TO_LOB. It is designed to convert a LONG or LONG RAW column to a CLOB or BLOB, respectively. Other methods are using PL/SQL, DBMS_XMLGEN. You can also use TO_LOB in insert statements.
Let's see how to convert LONG into CLOB-
SQL> CREATE TABLE t (x INT, y LONG);
Table created.
SQL>
SQL> INSERT INTO t VALUES (1, RPAD('*',9,'!'));
1 row created.
SQL> INSERT INTO t VALUES (2, RPAD('*',9,'#'));
1 row created.
SQL> INSERT INTO t VALUES (3, RPAD('*',9,'#'));
1 row created.
SQL> COMMIT;
Commit complete.
SQL>
So, we have table t with column y s LONG data type.
SQL> CREATE TABLE t1
2 AS
3 SELECT * FROM t
4 /
SELECT * FROM t
*
ERROR at line 3:
ORA-00997: illegal use of LONG datatype
SQL>
We can see the LONG restriction.
Let's use TO_LOB to convert it into CLOB.
SQL> CREATE TABLE t1
2 AS
3 SELECT x,
4 to_lob(y) as y
5 FROM t
6 /
Table created.
SQL> desc t1;
Name Null? Type
----------------------------------------------------- -------- ------------------------------------
X NUMBER(38)
Y CLOB
SQL>
Now you have the same table with the LONG column converted to CLOB.
this is stupid (as in probably not efficient) but it works for samll lengths of y (ie < 2000 characters)..
CREATE TABLE t (x INT, y LONG);
INSERT INTO t VALUES (1, RPAD('*',9,'!'));
CREATE TABLE t1
AS
SELECT x,
regexp_substr(SYS.DBMS_XMLGEN.GETXML('select y from t where rowid = '''||rowid||''''),'<Y>(.*)</Y>',1,1,'in',1) y
FROM t
/
it works by using dbms_xmlgen to generate a clob based on the LONG column.. then substr-ing the value back out.
this only works for small contents of the LONG column. but that is all i had and this worked for me.
I had a similar need, to list the objects and their sizes (including info on columns used in indexes), and came with this solution:
select idx1.table_owner owner, idx1.table_name, idx1.index_name, listagg(nvl(idx1.column_expression,idx1.column_name),',') within group (order by idx1.column_position) column_name
from xmltable(
'/ROWSET/ROW'
passing (select dbms_xmlgen.getxmltype('select ic.table_owner, ic.table_name, ic.index_name, ic.column_position, ic.column_name, ie.column_expression
from all_ind_columns ic
left outer join dba_ind_expressions ie on ie.table_owner=ic.table_owner and ie.table_name=ic.table_name and ie.index_name=ic.index_name and ie.column_position=ic.column_position') from dual)
columns index_name varchar2(30) path 'INDEX_NAME'
, table_owner varchar2(30) path 'TABLE_OWNER'
, table_name varchar2(30) path 'TABLE_NAME'
, column_position number path 'COLUMN_POSITION'
, column_name varchar2(30) path 'COLUMN_NAME'
, column_expression varchar2(4000) path 'COLUMN_EXPRESSION') idx1
group by idx1.table_owner, idx1.table_name, idx1.index_name

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

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;

SQL: how do I find if the contents of a varchar column are numeric?

One of the columns in my table is a varchar that is supposed to contain only numeric values (I can't change the definition to a number column). Thus my SQL query:
select to_number(col1) from tbl1 where ...
fails because for some row the contents of the column are not numeric.
What's a select query I can use to find these rows ?
I'm using an Oracle database
I'm looking for something like a is_number function.
There is no native "isnumeric" function in oracle, but this link shows you how to make one:
http://www.oracle.com/technetwork/issue-archive/o44asktom-089519.html
CREATE OR REPLACE FUNCTION isnumeric(p_string in varchar2)
RETURN BOOLEAN
AS
l_number number;
BEGIN
l_number := p_string;
RETURN TRUE;
EXCEPTION
WHEN OTHERS THEN
RETURN FALSE;
END;
/
I don't disagree that the best solution is to follow Tom Kyte's examples others have linked to already. However, if you just need something that is SQL only because you unfortunately do not have the relationship with your DBA to add pl/sql functions to your schema you could possibly leverage regular expressions to meet a basic need. Example:
select '234', REGEXP_SUBSTR('234','^\d*\.{0,1}\d+$') from dual
union all
select 'abc', REGEXP_SUBSTR('abc','^\d*\.{0,1}\d+$') from dual
union all
select '234234abc', REGEXP_SUBSTR('234234abc','^\d*\.{0,1}\d+$') from dual
union all
select '100.4', REGEXP_SUBSTR('100.4','^\d*\.{0,1}\d+$') from dual
union all
select '-100.4', REGEXP_SUBSTR('-100.4','^\d*\.{0,1}\d+$') from dual
union all
select '', REGEXP_SUBSTR('','^\d*\.{0,1}\d+$') from dual
Below is the output of the above:
INPUT RESULT
234 234
abc -
234234abc -
100.4 100.4
-100.4 -
Read this: http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:1794145000346577404
You may need to decide for yourself what is numeric.
Oracle will happily convert character strings in scientific notation (eg '1e10') to numbers, but it will baulk at something like '1,000' (because of the comma).