Oracle substitutes asterisk to all columns in a view - oracle

I've just bumped into the case when I have a view that has an asterisk reference to one of underlying tables. After compilation this asterisk sign is being exchanged to all column list and I don't want it to happen. See example below:
create or replace view test_view_cols as
select 1 f_1,
2 f_2,
3 f_3
from dual
/
create or replace view test_interface as
select d.*
from test_view_cols d
/
create or replace view test_view_cols as
select 1 f_1,
2 f_2,
3 f_3,
4 f_4,
5 f_5
from dual
/
select count(*) col_cnt -- returns 3 and not 5
from user_tab_cols s
where s.TABLE_NAME = 'TEST_INTERFACE';
select s.text
from user_views s
where s.VIEW_NAME = 'TEST_INTERFACE'
/* returns
select d."F_1",d."F_2",d."F_3"
from test_view_cols d
*/
But I don't want asterisk to be exchanged and I want have all the columns from test_view_cols in test_interface view.
Can I somehow force Oracle to keep an asterisk in an underlying query?
Oracle 11gR2

No.
You can't define a view that returns a variable number of columns depending on the changing definition of an underlying object (whether that underlying object is a table or a view). You could define a stored procedure that has an OUT parameter of type SYS_REFCURSOR that would return whatever columns are in the underlying object. You should also be able to define a pipelined table function that returns a different number of columns based on the underlying object-- that does get much easier in more recent versions, though.

You need after each change of the underlining object simple re-create the interface view as well.
create or replace view test_interface as
select d.*
from test_view_cols d
Than is the view definition again adjusted to the new object. The start * is expanded in the view compilation not in the query.
What you can use starting with Oracle 19c (or better 21c) is SQL Macro
The TABLE SQL Macros are defined similar as function, but they return text of the subquery that will be used.
You can define the SQL Macro that returns all columns of the base view as follows
create or replace function test_interface
return varchar2 SQL_MACRO
is
begin
return q'[
select * from test_view_cols]';
end;
/
You use the SQL Macro in the FROM clause
select * from test_interface();
This query return on the first run the three columns of the underlining view.
After the view is modified, you get back the five columns. Why?
The SQL Macro is activated only at parse time, so you should take some care, that you do not get the old columns state from the cached cursor.
But this is not a problem, because by changing the underlining view your cursor is automatically invalidated and must be reparsed with the new definition.
But SQL Macros are even more powerful. You can define one universal interface SQL Macro that can be applied on all your tested objects by passing the name of the underlining view or table as a parameter:
create function my_interface (t DBMS_TF.Table_t)
return varchar2 SQL_MACRO(TABLE)
is
begin
return q'[
select * from t]';
end;
/
In the query you than pass the name of the tested view
select * from my_interface(test_view_cols);
More information and examples of SQL Macros can be found here and there.

Related

Function results column names to be used in select statement

I have function which returns column names and i am trying to use the column name as part of my select statement, but my results are coming as column name instead of values
FUNCTION returning column name:
get_col_name(input1, input2)
Can И use this query to the results of the column from table -
SELECT GET_COL_NAME(input1,input2) FROM TABLE;
There are a few ways to run dynamic SQL directly inside a SQL statement. These techniques should be avoided since they are usually complicated, slow, and buggy. Before you do this try to find another way to solve the problem.
The below solution uses DBMS_XMLGEN.GETXML to produce XML from a dynamically created SQL statement, and then uses XML table processing to extract the value.
This is the simplest way to run dynamic SQL in SQL, and it only requires built-in packages. The main limitation is that the number and type of columns is still fixed. If you need a function that returns an unknown number of columns you'll need something more powerful, like the open source program Method4. But that level of dynamic code gets even more difficult and should only be used after careful consideration.
Sample schema
--drop table table1;
create table table1(a number, b number);
insert into table1 values(1, 2);
commit;
Function that returns column name
create or replace function get_col_name(input1 number, input2 number) return varchar2 is
begin
if input1 = 0 then
return 'a';
else
return 'b';
end if;
end;
/
Sample query and result
select dynamic_column
from
(
select xmltype(dbms_xmlgen.getxml('
select '||get_col_name(0,0)||' dynamic_column from table1'
)) xml_results
from dual
)
cross join
xmltable
(
'/ROWSET/ROW'
passing xml_results
columns dynamic_column varchar2(4000) path 'DYNAMIC_COLUMN'
);
DYNAMIC_COLUMN
--------------
1
If you change the inputs to the function the new value is 2 from column B. Use this SQL Fiddle to test the code.

Create Type based on an exiting Table

As the title said : I want to create a type in oracle based on an existing Table.
I did as follow :
create or replace type MY_NEW_TYPE as object( one_row EXISTING_TABLE%rowtype);
The Aim is to be able to use this into a function which will return a table containing sample row of the table EXISTING_TABLE :
create or replace function OUTPUT_FCT() return MY_NEW_TYPE AS
...
If you only need to create a function that returns a row from your table, you could try something like the following, without creating types.
setup:
create table EXISTING_TABLE( a number, b varchar2(100));
insert into EXISTING_TABLE values (1, 'one');
function:
create or replace function OUTPUT_FCT return EXISTING_TABLE%rowtype AS
retVal EXISTING_TABLE%rowType;
begin
select *
into retVal
from EXISTING_TABLE
where rownum = 1;
--
return retVal;
end;
function call
SQL> begin
2 dbms_output.put_line(OUTPUT_FCT().a);
3 dbms_output.put_line(OUTPUT_FCT().b);
4 end;
5 /
1
one
However, I would not recommend such an approach, because things like select * can be really dangerous; I would much prefer defining a type with the fields I need, and then explicitly query my table for the needed columns.
No, you can't do that, you'll get a compilation error:
create or replace type my_new_type as object(one_row t42%rowtype);
/
Type MY_NEW_TYPE compiled
Errors: check compiler log
show errors
Errors for TYPE STACKOVERFLOW.MY_NEW_TYPE:
LINE/COL ERROR
-------- -----------------------------------------------------------------------
0/0 PL/SQL: Compilation unit analysis terminated
1/36 PLS-00329: schema-level type has illegal reference to MYSCHEMA.T42
You will need to specify each field in the object type, and you will have to specify the data types manually too - you can't use table.column%type either.
You could create the type dynamically based on column and data type information from the data dictionary, but as this will (hopefully) be a one-off task and not something you'd do at runtime, that doesn't really seem worth it.
You can create a PL/SQL table type based on your table's rowtype, but you would only be able to call a function returning that from PL/SQL, not from plain SQL - so you couldn't use it in a table collection expression for example. If you were only returning a single sample row you could return a record rather than a table, but the same applies. You can also have a function that returns a ref cursor which could match the table's structure, but you wouldn't be able to treat that as a table either.
Read more about object type creation in the documentation. Specifically the attribute and datatype sections.

Parse a parameter to a view from a SP in Oracle 10g

I have created a VIEW by joining some number of tables. I used a SP to call that VIEW and in the SP I'm filtering the data set of the view using an ID. Now I need to pass this ID to VIEW and do the filtering inside the VIEW itself. What is the way of passing this ID as a parameter to view in Oracle 10g?
Current View
CREATE OR REPLACE FORCE VIEW "MY_VIEW"
//SELECT statements goes here
FROM MY_TABLE_1, MY_TABLE_2
//TABLE JOINS
where
//FILTERS
Current Stored Procedure
CREATE OR REPLACE PROCEDURE MY_SP
(
REQUESTACCOUNTID IN NUMBER
, p_cursor out SYS_REFCURSOR
) AS
internal_flag NUMBER;
BEGIN
open p_cursor for
SELECT //SELECT THE COLUMNS
from MY_VIEW my
WHERE my.account_id = REQUESTACCOUNTID;
END MY_SP;
What I need to do is, parse the parameter REQUESTACCOUNTID to the view while selecting
this is sorted out using a package variable. More explanation can be found using this URL

PL/SQL procedure - too many values

I'm sure this is something simple, but I'm really new to PL/SQL and this has me stuck.
I've written a simple stored procedure to return a few values about a customer. Right off the bat, the %rowtype's are not coming up as reserved keywords but the compiler isn't flagging those as errors.
It is, however, ignoring the entire SQL statement flagging the line FROM demo_customers as too many values. Even if I try reducing it to only select one column it still gives me the same error.
create or replace
PROCEDURE GETCUSTOMER
(
arg_customerID demo_customers.customer_id%type,
returnRec OUT demo_customers%rowtype
)
AS
BEGIN
SELECT customer_id, cust_first_name, cust_last_name, cust_email
INTO returnRec
FROM demo_customers
WHERE customer_id = arg_customerID ;
END GETCUSTOMER;
If you want to select into a %ROWTYPE record, you'll want to do a SELECT * rather than selecting individual columns
create or replace
PROCEDURE GETCUSTOMER
(
arg_customerID demo_customers.customer_id%type,
returnRec OUT demo_customers%rowtype
)
AS
BEGIN
SELECT *
INTO returnRec
FROM demo_customers
WHERE customer_id = arg_customerID ;
END GETCUSTOMER;
If you select 4 columns explicitly, Oracle expects you to have 4 variables to select those values into.

Table-Valued Functions in ORACLE 11g ? ( parameterized views )

I've seen discussions about this in the past, such as here. But I'm wondering if somewhere along the line, maybe 10g or 11g (we are using 11g), ORACLE has introduced any better support for "parameterized views", without needing to litter the database with all sorts of user-defined types and/or cursor definitions or sys_context variables all over.
I'm hoping maybe ORACLE's added support for something that simply "just works", as per the following example in T-SQL:
CREATE FUNCTION [dbo].[getSomeData] (#PRODID ROWID)
RETURNS TABLE AS
RETURN SELECT PRODID, A, B, C, D, E
FROM MY_TABLE
WHERE PRODID = #PRODID
Then just selecting it as so:
SELECT * FROM dbo.getSomeData(23)
No need for SYS_CONTEXT or cursor definitions.
You do need a type so that, when the SQL is parsed, it can determine which columns are going to be returned.
That said, you can easily write a script that will generate type and collection type definitions for one or more tables based on the data in user_tab_columns.
The closest is
create table my_table
(prodid number, a varchar2(1), b varchar2(1),
c varchar2(1), d varchar2(1), e varchar2(1));
create type my_tab_type is object
(prodid number, a varchar2(1), b varchar2(1),
c varchar2(1), d varchar2(1), e varchar2(1))
.
/
create type my_tab_type_coll is table of my_tab_type;
/
create or replace function get_some_data (p_val in number)
return my_tab_type_coll pipelined is
begin
FOR i in (select * from my_table where prodid=p_val) loop
pipe row(my_tab_type(i.prodid,i.a,i.b,i.c,i.d,i.e));
end loop;
return;
end;
/
SELECT * FROM table(get_Some_Data(3));
It is possible to define a kind of "parametrized" views in Oracle.
The steps are:
Define a package containing as public members that are in fact the needed parameters (there is no need for functions or procedures in that package),
Define a view that is based on that package members.
To use this mechanism one user should:
open a session,
assign the desired values to that package members,
SELECT data from the view,
do other stuff or close the session.
REMARK: it is essential for the user to do all the three steps in only one session as the package members scope is exactly a session.
There are TWO types of table-valued functions in SQL SERVER:
Inline table-valued function: For an inline table-valued function, there is no function body; the table is the result set of a single SELECT statement. This type can be named as
'parameterized view' and it has no equivalent in ORACLE as I know.
Multistatement table-valued function: For a multistatement table-valued function, the function body, defined in a BEGIN...END block, contains a series of Transact-SQL statements that build and insert rows into the table that will be returned.
The above sample (By Gary Myers) creates a table function of the second type and it is NOT a 'parameterized view'.

Resources