Oracle DB: Data type conversion problem when using a union - oracle

So, I have two tables in an Oracle database. The relevant column (let's call it field_x) is of type CLOB in table_a and VARCHAR2(510) in table_b.
If I run any of these queries, everything works fine:
SELECT cast(field_x AS VARCHAR2(510)) AS x FROM table_a WHERE some-condition;
SELECT field_x AS x FROM table_b WHERE some-condition;
But if I combine the output of those same query with a union, a problem arises:
SELECT cast(field_x AS VARCHAR2(510)) AS x FROM table_a WHERE some-condition
UNION
SELECT field_x AS x FROM table_b WHERE some-condition;
I'm getting this error:
ORA-12801: error signaled in parallel query server P00M, instance such-and-such
ORA-22835: Buffer too small for CLOB to CHAR or BLOB to RAW conversion (actual: 4718, maximum: 4000)
What could be the reason? Each query works fine on its own, so it's not like there are any clob values longer than 510 characters.

It works with DBMS_LOB.substr explicitly called:
SELECT cast(DBMS_LOB.substr(field_x, 510, 1) AS VARCHAR2(510)) AS x FROM table_a WHERE some-condition
UNION
SELECT field_x AS x FROM table_b WHERE some-condition;

Related

Oracle: clob column inconsistent with itself in UNION statement ORA-00932

I'm getting an ORA-00932 doing the UNION of 2 tables in Oracle. I don't have much experience with this database.
I reduced the problem till the union of the CLOB column with itself, but still get the error. Here is the command:
SELECT nm_wkt FROM UR_C99.CD_VET
UNION
SELECT nm_wkt FROM UR_C99.CD_VET
And I get the error (in Portuguese):
SQL Error [932] [42000]: ORA-00932: tipos de dados inconsistentes: esperava - obteve CLOB
The column definition is just nm_wkt CLOB NOT NULL.
I wasn't expecting to get the error at this point. It's just a simple union statement. I'll need to redo a lot of work if I can't make this union.
Any help?
I solved it. Must use UNION ALL so the clob columns won't be compared:
SELECT nm_wkt FROM UR_C39.CD_VET_GEOBNDES
UNION all
SELECT nm_wkt FROM UR_C39.CD_VET_GEOBNDES

MINUS not working for the same value in Snowflake

I loaded data from oracle to snowflake in table1 using informatica.
and the same data we have in snowflake table already table2.
i want to perform minus query for testing but it doesn't work as expected.
eg col1 field value is 1.21 in table1 and the datatype is same as snowflake table2.
col1 fields value is 1.21 in table2
when i run
select col1 form table1
minus
select col1 from table2
it gives two rows but when we check those records value is same.
What could be this issue ?
Any leads are appreciated.
Thanks .
given
select 1.21
minus
select 1.21;
gives no rows, it suspect it's a type thing.
but
select 1.2100000000000001::double as a
minus
select 1.2099999999999999::double as a;
gives 1.21, but also if you output those values you see
select 1.2100000000000001::double as a
union
select 1.2099999999999999::double as a;
values:
1.21
1.21
which is the default format of 6 decimal places of these values
adding one more level of decimal places there is magically no difference..
select 1.21000000000000001::double as a
minus
select 1.20999999999999999::double as a;
basically floating point number are not how they appear. ether cast them to some smaller type like
select 1.2100000000000001::number(30,3) as a
minus
select 1.2099999999999999::number(30,3) as a;
gives no "difference"

Invalid DataType for one value

I have an odd scenario. On an oracle 11.2 db there is one value that when selected into a table type causes and invalid data type error when the table type is used. I have validated that when the row is excluded everything else works fine.
Pseudo code;
type my_nums is table of number;
select num bulk collect into my_nums from tableA;
select t.my_col from tableB t where t.my_col IN (select column_value from table(my_nums));
I have checked this one key from tableA is a numeric using;
with t as (select to_char(num) as txt from tableA where num = 33)
select txt, case when regexp_like(txt, '^-?[[:digit:],.]*$') then 'Numeric' else 'Non-Numeric' end as type
FROM t;
Taken from How to check if a field is numeric. Is there something else I can look at to find out why this is happening?
To be clear, using the following, all is well in my procedure.
select num bulk collect into my_nums from tableA where num != 33;
Thanks in advance.

Oracle CLOB column and LAG

I'm facing a problem when I try to use LAG function on CLOB column.
So let's assume we have a table
create table test (
id number primary key,
not_clob varchar2(255),
this_is_clob clob
);
insert into test values (1, 'test1', to_clob('clob1'));
insert into test values (2, 'test2', to_clob('clob2'));
DECLARE
x CLOB := 'C';
BEGIN
FOR i in 1..32767
LOOP
x := x||'C';
END LOOP;
INSERT INTO test(id,not_clob,this_is_clob) values(3,'test3',x);
END;
/
commit;
Now let's do a select using non-clob columns
select id, lag(not_clob) over (order by id) from test;
It works fine as expected, but when I try the same with clob column
select id, lag(this_is_clob) over (order by id) from test;
I get
ORA-00932: inconsistent datatypes: expected - got CLOB
00932. 00000 - "inconsistent datatypes: expected %s got %s"
*Cause:
*Action:
Error at Line: 1 Column: 16
Can you tell me what's the solution of this problem as I couldn't find anything on that.
The documentation says the argument for any analytic function can be any datatype but it seems unrestricted CLOB is not supported.
However, there is a workaround:
select id, lag(dbms_lob.substr(this_is_clob, 4000, 1)) over (order by id)
from test;
This is not the whole CLOB but 4k should be good enough in many cases.
I'm still wondering what is the proper way to overcome the problem
Is upgrading to 12c an option? The problem is nothing to do with CLOB as such, it's the fact that Oracle has a hard limit for strings in SQL of 4000 characters. In 12c we have the option to use extended data types (providing we can persuade our DBAs to turn it on!). Find out more.
Some of the features may not work properly in SQL when using CLOBs(like DISTINCT , ORDER BY GROUP BY etc. Looks like LAG is also one of them but, I couldn't find anywhere in docs.
If your values in the CLOB columns are always less than 4000 characters, you may use TO_CHAR
select id, lag( TO_CHAR(this_is_clob)) over (order by id) from test;
OR
convert it into an equivalent SELF JOIN ( may not be as efficient as LAG )
SELECT a.id,
b.this_is_clob AS lagging
FROM test a
LEFT JOIN test b ON b.id < a.id;
Demo
I know this is an old question, but I think I found an answer which eliminates the need to restrict the CLOB length and wanted to share it. Utilizing CTE and recursive subqueries, we can replicate the lag functionality with CLOB columns.
First, let's take a look at my "original" query:
WITH TEST_TABLE AS
(
SELECT LEVEL ORDER_BY_COL,
TO_CLOB(LEVEL) AS CLOB_COL
FROM DUAL
CONNECT BY LEVEL <= 10
)
SELECT tt.order_by_col,
tt.clob_col,
LAG(tt.clob_col) OVER (ORDER BY tt.order_by_col)
FROM test_table tt;
As expected, I get the following error:
ORA-00932: inconsistent datatypes: expected - got CLOB
Now, lets look at the modified query:
WITH TEST_TABLE AS
(
SELECT LEVEL ORDER_BY_COL,
TO_CLOB(LEVEL) AS CLOB_COL
FROM DUAL
CONNECT BY LEVEL <= 10
),
initial_pull AS
(
SELECT tt.order_by_col,
LAG(tt.order_by_col) OVER (ORDER BY tt.order_by_col) AS PREV_ROW,
tt.clob_col
FROM test_table tt
),
recursive_subquery (order_by_col, prev_row, clob_col, prev_clob_col) AS
(
SELECT ip.order_by_col, ip.prev_row, ip.clob_col, NULL
FROM initial_pull ip
WHERE ip.prev_row IS NULL
UNION ALL
SELECT ip.order_by_col, ip.prev_row, ip.clob_col, rs.clob_col
FROM initial_pull ip
INNER JOIN recursive_subquery rs ON ip.prev_row = rs.order_by_col
)
SELECT rs.order_by_col, rs.clob_col, rs.prev_clob_col
FROM recursive_subquery rs;
So here is how it works.
I create the TEST_TABLE, this really is only for the example as you should already have this table somewhere in your schema.
I create a CTE of the data I want to pull, plus a LAG function on the primary key (or a unique column) in the table partitioned and ordered in the same way I would have in my original query.
Create a recursive subquery using the initial row as the root and descending row by row joining on the lagged column. Returning both the CLOB column from the current row and the CLOB column from its parent row.

Oracle identity column and insert into select

Oracle 12 introduced nice feature (which should have been there long ago btw!) - identity columns. So here's a script:
CREATE TABLE test (
a INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
b VARCHAR2(10)
);
-- Ok
INSERT INTO test (b) VALUES ('x');
-- Ok
INSERT INTO test (b)
SELECT 'y' FROM dual;
-- Fails
INSERT INTO test (b)
SELECT 'z' FROM dual UNION ALL SELECT 'zz' FROM DUAL;
First two inserts run without issues providing values for 'a' of 1 and 2. But the third one fails with ORA-01400: cannot insert NULL into ("DEV"."TEST"."A"). Why did this happen? A bug? Nothing like this is mentioned in the documentation part about identity column restrictions. Or am I just doing something wrong?
I believe the below query works, i havent tested!
INSERT INTO Test (b)
SELECT * FROM
(
SELECT 'z' FROM dual
UNION ALL
SELECT 'zz' FROM dual
);
Not sure, if it helps you any way.
For, GENERATED ALWAYS AS IDENTITY Oracle internally uses a Sequence only. And the options on general Sequence applies on this as well.
NEXTVAL is used to fetch the next available sequence, and obviously it is a pseudocolumn.
The below is from Oracle
You cannot use CURRVAL and NEXTVAL in the following constructs:
A subquery in a DELETE, SELECT, or UPDATE statement
A query of a view or of a materialized view
A SELECT statement with the DISTINCT operator
A SELECT statement with a GROUP BY clause or ORDER BY clause
A SELECT statement that is combined with another SELECT statement with the UNION, INTERSECT, or MINUS set operator
The WHERE clause of a SELECT statement
DEFAULT value of a column in a CREATE TABLE or ALTER TABLE statement
The condition of a CHECK constraint
The subquery and SET operations rule above should answer your Question.
And for the reason for NULL, when pseudocolumn(eg. NEXTVAL) is used with a SET operation or any other rules mentioned above, the output is NULL, as Oracle couldnt extract them in effect with combining multiple selects.
Let us see the below query,
select rownum from dual
union all
select rownum from dual
the result is
ROWNUM
1
1

Resources