Oracle COLLECT function and types - oracle

I've got an issue with Oracle 10g and the use of the COLLECT function. I only found out about its existence this morning but have a problem which could be solved by using it in association with the member of condition.
Initially I wrote the code shown below, which came back with the error "ORA_00932: inconsistent datatypes: expected UDT got -".
with my_tab as (
select 1 as cola, 1 as colb from dual union all
select 1 as cola, 2 as colb from dual union all
select 2 as cola, 3 as colb from dual union all
select 2 as cola, 4 as colb from dual union all
select 3 as cola, 3 as colb from dual union all
select 3 as cola, 4 as colb from dual union all
select 4 as cola, 1 as colb from dual union all
select 4 as cola, 2 as colb from dual
)
select
cola,
colb_vals
from (
select
cola,
collect(colb) as colb_vals
from my_tab
group by cola
)
where 2 member of colb_vals
I found this a little strange since in Oracle 10.2.4.0, it seems that the database will create a temporary system generated user defined type, and use that. If I remove the condition, (where 2 member of colb_vals) then the code will run and show the data retrieved included the temporary UDT (named SYSTPblahblahblah==).
After a bit more searching, I realised that I could solve this be using CREATE TYPE and then using the CAST function to change the type of the nested table. Which worked.
This was using CREATE TYPE number_ntt as TABLE OF NUMBER; and replacing collect(colb) with cast(collect(colb) as number_ntt)
I then tried to use a nested table type created in a package, since I only need this type to be available for one particular query in one procedure in a single package. And I couldn't get it to work.
create or replace package mike_temp_pkg as
type number_ntt is table of number;
end mike_temp_pkg;
And this time replacing collect(colb) with cast(collect(colb) as mike_temp_pkg.number_ntt)
This resulted in ORA-00932: invalid datatype.
So the question I have is in two parts really:
Why does the system generated user
defined type work for the select
but not for the member of?
Why does the type need to be a SQL
type and not a PL/SQL type in a
package? I don't really define types
that often so there might be a
simple answer to that question.

(1)
The COLLECT function documentation states "To get the results of this function you must use it within a CAST function." I suspect it simply is not designed to support any uses, except a simple dump of its contents, unless you cast it to a defined type.
(2)
The SQL parser has no knowledge of or access to types defined in PL/SQL blocks. Even when you execute a SQL statement inside of PL/SQL code, the statement is essentially handed off to an independent parser (with PL/SQL variable names replaced by bind variable placeholders).

Related

Strange Behaviour Related to ORA-01400 Error [duplicate]

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

Internal Functionality of DUAL table?

Will local database get disturb if we create DUAL table ?
Kindly Suggest me ?
create table DUAL
(
x varchar2(1)
);
No you cannot create a dual table. DUAL table is owned by SYS and SYS owns the data dictionary so you can not create it.
See the wiki
The DUAL table is a special one-row table present by default in all
Oracle database installations. It is suitable for use in selecting a
pseudocolumn such as SYSDATE or USER. The table has a single
VARCHAR2(1) column called DUMMY that has a value of 'X'.
Even if you try to create a DUAL table then it will create problems for you as everytime the Oracle engine has to ensure that you are not calling the SYS dual table. You need to specify the database and schema as well. It may lead to too much of ambiguity problem for Oracle engine. The Oracle optimizer knows everything that DUAL does and what it should do and it then does things based on that.
SQL Reference:
DUAL is a table automatically created by Oracle Database along with
the data dictionary. DUAL is in the schema of the user SYS but is
accessible by the name DUAL to all users. It has one column, DUMMY,
defined to be VARCHAR2(1), and contains one row with a value X.
Selecting from the DUAL table is useful for computing a constant
expression with the SELECT statement. Because DUAL has only one row,
the constant is returned only once. Alternatively, you can select a
constant, pseudocolumn, or expression from any table, but the value
will be returned as many times as there are rows in the table. Refer
to "About SQL Functions" for many examples of selecting a constant
value from DUAL.
Beginning with Oracle Database 10g Release 1, logical I/O is not
performed on the DUAL table when computing an expression that does not
include the DUMMY column. This optimization is listed as FAST DUAL in
the execution plan. If you SELECT the DUMMY column from DUAL, then
this optimization does not take place and logical I/O occurs.
Will local database get disturb if we create DUAL table ?
Yes, of course weird things can and will happen. DUAL is owned by SYS. SYS owns the data dictionary, therefore DUAL is part of the data dictionary. You are not to modify the data dictionary via SQL ever.
And the first question is "How will you guarantee only one row in your own DUAL table"?
This goes back to the original article Self-Managing PL/SQL by Steven Feuerstein where he explains "Use Your Own DUAL Table". But, that was back then when DUAL table was prone to such things.
However, in the recent releases, the DUAL table structure has been made robust and you cannot have more than single row ever. Here is a proof:
SQL> conn sys#pdborcl as sysdba
Enter password:
Connected.
SQL> insert into dual select * from dual;
1 row created.
SQL> select * from dual;
D
-
X
I know, few would argue that we can handle one row with our own DUAL table using a trigger or ROWNUM =1, however, you will soon realize the cons. It is simply not necessary from 10g on wards, as the DUAL table is now a memory structure and you cannot add a row to it as demonstrated above.
Imagine a situation where you have created your own DUAL table, and you are using the call to DUAL table in your PL/SQL code to get the USER, SYSDATE, SYSTIMESTAMP etc.
This is the code taken from the stdbody.sql file delivered with Oracle Database:
1 FUNCTION USER
2 RETURN VARCHAR2
3 IS
4 c VARCHAR2 (255);
5 BEGIN
6 SELECT USER
7 INTO c
8 FROM SYS.DUAL;
9
10 RETURN c;
11 END;
If you ever have more than one row in your own DUAL table, every call to the USER function in your PL/SQL code will fail with TOO_MANY_ROWS error.
Bottomline : All the discussion about using your own DUAL table made sense back then before 10g days. The DUAL table is now a robust memory structure and doesn't allow to add a row to it. So, makes no sense to use your own DUAL table rather than the SYS.DUAL.

Oracle, need help understanding to_number and to_date functions

I been working with a complex view written by some other company in 2005. I am trying to understand what it is doing for reasons beyond this post. By the highly complex nature of this view (over 500 lines of code) I take it that the writers new what they where doing.
I keep finding things like TO_NUMBER(null), TO_DATE(null) in various places.
Seems to me like totally unnecessary use of a function.
Is there any technical reasons or advantages that justify why this was design like this?
By default NULL does not have a data type:
SQL> select dump(null) from dual;
DUMP
----
NULL
SQL>
However, if we force Oracle into making a decision it will default to making it a string:
SQL> create or replace view v1 as
select 1 as id
, null as dt
from dual
/
2 3 4 5
View created.
SQL> desc v1
Name Null? Type
-------------- -------- ----------------------------
ID NUMBER
DT VARCHAR2
SQL>
But this not always desirable. We might need to use NULL in a view for a number of reasons (defining an API, filling out a jagged UNION, etc) and so we cast the NULL to another datatype to get the projection we need.
SQL> create or replace view v1 as
select 1 as id
, to_date(null) as dt
from dual
/
2 3 4 5
View created.
SQL> desc v1
Name Null? Type
-------------- -------- ----------------------------
ID NUMBER
DT DATE
SQL>
Later versions have got smarter with regards to handling UNION. On my 11gR2 database, even though I use the null in first declared query (and that usually drives things) I still get the correct datatype:
SQL> create or replace view v1 as
select 1 as id
, null as dt
from dual
union all
select 2 as id
, sysdate as something_else
from dual
/
2 3 4 5 6 7 8 9
View created.
SQL>
SQL> desc v1
Name Null? Type
-------------- -------- ----------------------------
ID NUMBER
DT DATE
SQL>
Explicitly casting NULL may be left over from 8i, or to workaround a bug, or as ammoQ said, "superstitious".
In some old and rare cases the implicit conversion of NULL in set operations caused errors like ORA-01790: expression must have same datatype as corresponding expression.
I can't find any great references for this old behavior, but Google returns a few results that claim a query like this would fail in 8i:
select 'a' a from dual
union
select null a from dual;
And there is at least one similar bug, "Bug 9456979 Wrong result from push of NVL / DECODE into UNION view with NULL select list item - superceded".
But don't let 16 year-old software and some rare bug dictate how to program. And don't think there's a positive correlation between code size programming skill. There's a negative correlation: good programmers will create smaller, more readable code, and won't leave as many mysteries for future coders.
Well, I would prefer CAST(NULL AS DATE) or CAST(NULL AS NUMBER) instead of TO_DATA(NULL), looks more logical in my eyes.
I know two scenarios where such an expression is required.
One it the case of UNION, as already stated in the other aswers.
Another scenario is the case of overloaded procedures/functions, for example:
CREATE OR REPLACE PROCEDURE MY_PROC(val IN DATE) AS
BEGIN
DELETE FROM EMP WHERE HIRE_DATE = val;
END;
/
CREATE OR REPLACE PROCEDURE MY_PROC(val IN NUMBER) AS
BEGIN
DELETE FROM EMP WHERE EMP_ID = val;
END;
/
Calling the procedure like MY_PROC(NULL); does not work, Oracle does not know which procedure to execute. You must call it like MY_PROC(CAST(NULL AS DATE)); for example.
As a oracle PL/SQL programmer, I really don't find any logical reason for doing the things you have specified. The only logical approach to deal with null in oracle is to use nvl(), I really don't find any reason to use TO_NUMBER(null), TO_DATE(null) in a complex view.

ORA-00907: missing right parenthesis when create varray

In my program there are a lot of situation when i need to get additional information about knowing ids. So i have list of ids, which length may be very long (for example 100000 elements in it).
How i can use this list and transfer in oracle for getting sql without using temp tables?
No i try to use collection:
CREATE TYPE TEST_VARRAY IS VARRAY(5000) OF NUMBER(18);
SELECT G.ID, G.NAME FROM ANY_TABLE G
WHERE G.ID IN
(
SELECT COLUMN_VALUE FROM TABLE(
NEW TEST_VARRAY
(0,1,2,3... and so on ...,995,996,997,998,999)
)
);
there are 1000 numbers. And when I try execute this query the error ORA-00907: missing right parenthesis tips is appeared! But if i delete first 0 (so we have 999 numbers) the sql is executed ok.
What is problem here?
There is a limit in Oracle IN clause.
A comma-delimited list of expressions can contain no more than 1000
expressions. A comma-delimited list of sets of expressions can contain
any number of sets, but each set can contain no more than 1000
expressions.
Read here or here or here
In my opinion, you are misusing collections, at least I am not sure something like you did is good.
As far as I understand you generate this query before run, so what is the problem to do like that?
with ids as (select /*+ materialize */ 1 id from dual union all
select 2 from dual union all
select 3 from dual union all
select 4 from dual union all
/* repeat with the ids you need */
select 9999 from dual)
select *
from yourTable, ids
where yourTable.id = ids.id;
And that's it! Without any limitations, with pure SQL only. I have added materialize hint to ensure it is not performance relevant, but I think it can be skipped.
No temporary tables, no collections, nothing to create and support. Just SQL.
If you will put ids out of with into from clause it will work in any RDBMS (I guess).

What is the dual table in Oracle?

I've heard people referring to this table and was not sure what it was about.
It's a sort of dummy table with a single record used for selecting when you're not actually interested in the data, but instead want the results of some system function in a select statement:
e.g. select sysdate from dual;
See http://www.adp-gmbh.ch/ora/misc/dual.html
As of 23c, Oracle supports select sysdate /* or other value */, without from dual, as has been supported in MySQL for some time already.
It is a dummy table with one element in it. It is useful because Oracle doesn't allow statements like
SELECT 3+4
You can work around this restriction by writing
SELECT 3+4 FROM DUAL
instead.
From Wikipedia
History
The DUAL table was created by Chuck Weiss of Oracle corporation to provide a table for joining in internal views:
I created the DUAL table as an underlying object in the Oracle Data Dictionary. It was never meant to be seen itself, but instead used
inside a view that was expected to be queried. The idea was that you
could do a JOIN to the DUAL table and create two rows in the result
for every one row in your table. Then, by using GROUP BY, the
resulting join could be summarized to show the amount of storage for
the DATA extent and for the INDEX extent(s). The name, DUAL, seemed
apt for the process of creating a pair of rows from just one. 1
It may not be obvious from the above, but the original DUAL table had two rows in it (hence its name). Nowadays it only has one row.
Optimization
DUAL was originally a table and the database engine would perform disk IO on the table when selecting from DUAL. This disk IO was usually logical IO (not involving physical disk access) as the disk blocks were usually already cached in memory. This resulted in a large amount of logical IO against the DUAL table.
Later versions of the Oracle database have been optimized and the database no longer performs physical or logical IO on the DUAL table even though the DUAL table still actually exists.
I think this wikipedia article may help clarify.
http://en.wikipedia.org/wiki/DUAL_table
The DUAL table is a special one-row
table present by default in all Oracle
database installations. It is suitable
for use in selecting a pseudocolumn
such as SYSDATE or USER The table has
a single VARCHAR2(1) column called
DUMMY that has a value of "X"
It's the special table in Oracle. I often use it for calculations or checking system variables. For example:
Select 2*4 from dual prints out the result of the calculation
Select sysdate from dual prints the server current date.
A utility table in Oracle with only 1 row and 1 column. It is used to perform a number of arithmetic operations and can be used generally where one needs to generate a known output.
SELECT * FROM dual;
will give a single row, with a single column named "DUMMY" and a value of "X" as shown here:
DUMMY
-----
X
Kind of a pseudo table you can run commands against and get back results, such as sysdate. Also helps you to check if Oracle is up and check sql syntax, etc.
The DUAL table is a special one-row table present by default in all Oracle database installations. It is suitable for use in selecting a pseudocolumn such as SYSDATE or USER
The table has a single VARCHAR2(1) column called DUMMY that has a value of "X"
You can read all about it in http://en.wikipedia.org/wiki/DUAL_table
DUAL is necessary in PL/SQL development for using functions that are only available in SQL
e.g.
DECLARE
x XMLTYPE;
BEGIN
SELECT xmlelement("hhh", 'stuff')
INTO x
FROM dual;
END;
More Facts about the DUAL....
http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:1562813956388
Thrilling experiments done here, and more thrilling explanations by Tom
DUAL we mainly used for getting the next number from the sequences.
Syntax : SELECT 'sequence_name'.NEXTVAL FROM DUAL
This will return the one row one column value(NEXTVAL column name).
another situation which requires select ... from dual is when we want to retrieve the code (data definition) for different database objects (like TABLE, FUNCTION, TRIGGER, PACKAGE), using the built in DBMS_METADATA.GET_DDL function:
select DBMS_METADATA.GET_DDL('TABLE','<table_name>') from DUAL;
select DBMS_METADATA.GET_DDL('FUNCTION','<function_name>') from DUAL;
in is true that nowadays the IDEs do offer the capability to view the DDL of a table, but in simpler environments like SQL Plus this can be really handy.
EDIT
a more general situation: basically, when we need to use any PL/SQL procedure inside a standard SQL statement, or when we want to call a procedure from the command line:
select my_function(<input_params>) from dual;
both recipes are taken from the book 'Oracle PL/SQL Recipes' by Josh Juneau and Matt Arena
The DUAL is special one row, one column table present by default in all Oracle databases. The owner of DUAL is SYS.
DUAL is a table automatically created by Oracle Database along with the data functions. It is always used to get the operating systems functions(like date, time, arithmetic expression., etc.)
SELECT SYSDATE from dual;
It's a object to put in the from that return 1 empty row. For example:
select 1 from dual;
returns 1
select 21+44 from dual;
returns 65
select [sequence].nextval from dual;
returns the next value from the sequence.

Resources