How do i make the following wrapper select all columns "*" instead of just if_type, and number_infected?
--spec
create or replace package WrapperSample is
type TResultRow is record(
if_type codes.cd%type
,number_infected Integer);
type TResultRowList is table of TResultRow;
function GetADedIcWarningsProv
(
p_hos_id in work_entity_data.hos_id%type
,p_date in date
) return TResultRowList
pipelined;
end WrapperSample;
/
--body
create or replace package body WrapperSample is
function GetADedIcWarningsProv
(
p_hos_id in work_entity_data.hos_id%type
,p_date in date
) return TResultRowList
pipelined is
v_refcur eOdatatypes_package.eOrefcur;
currentRow TResultRow;
begin
v_refcur := YourSchema.getADedIcWarningsProv(p_hos_id, p_date);
loop
fetch v_refcur
INTO currentRow;
exit when v_refcur%NotFound;
pipe row(currentRow);
end loop;
close v_refcur;
return;
end;
end WrapperSample;
/
I am not sure if I do understand your question and requirement.
But if you're looking for a way to get a table's content, or a portion thereof, this is probably how you would approach it:
create table tq84_test_table (
col_1 number,
col_2 varchar2(10),
col_3 date
);
insert into tq84_test_table values (1, 'one' , sysdate);
insert into tq84_test_table values (2, 'two' , sysdate+1);
insert into tq84_test_table values (3, 'three', sysdate-1);
create or replace package tq84_sss as
type record_t is table of tq84_test_table%rowtype;
function GetADedIcWarningsProv return record_t;
end;
/
create or replace package body tq84_sss as
function GetADedIcWarningsProv return record_t
is
ret record_t;
begin
select * bulk collect into ret
from tq84_test_table;
return ret;
end GetADedIcWarningsProv;
end;
/
You would then later use this function like so:
declare
table_content tq84_sss.record_t;
begin
table_content := tq84_sss.GetADedIcWarningsProv;
for i in 1 .. table_content.count loop
dbms_output.put_line(table_content(i).col_1 || ' ' ||
table_content(i).col_2 || ' ' ||
table_content(i).col_3
);
end loop;
end;
/
just use %rowtype
declare
...
someTableRow someTable%rowtype;
...
begin
select * into someTableRow from someTable where blah;
...
Related
I have developed a function to UNION ALL tables from a list of table names (a table called tablelist below) inspired by this SO post.
The initial function just returns a selection, but now I'd like to write a new table with a name taken from a parameter new_table_name.
I'm struggling with the syntax to insert the parameter into the DROP TABLE AND CREATE TABLE statements. Here's one of the attempts which returns ERROR: mismatched parentheses at or near ";"
DROP FUNCTION IF EXISTS f_multi_union(text);
CREATE OR REPLACE FUNCTION f_multi_union(new_tab_name text)
RETURNS Table (my_id int, metric double precision, geom geometry)
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY EXECUTE
(
DROP TABLE IF EXISTS working.'' || new_tab_name || '';
CREATE TABLE working.'' || new_tab_name || '' AS (
SELECT string_agg(format('SELECT * FROM %s', tbl), ' UNION ALL ')
FROM (SELECT tbl FROM working.tablelist) sub
)
);
END
$func$;
Something like this?
DROP FUNCTION IF EXISTS f_multi_union(text);
CREATE OR REPLACE FUNCTION f_multi_union(new_tab_name text)
RETURNS void -- nothing to return
LANGUAGE plpgsql AS
$func$
DECLARE
_sql TEXT;
BEGIN
_sql := format('DROP TABLE IF EXISTS working.%I;', new_tab_name); -- avoid SQL injection
EXECUTE _sql;
_sql := 'SELECT string_agg(format(''SELECT * FROM %I'', tbl), '' UNION ALL '')
FROM (SELECT tbl FROM working.tablelist) sub;';
EXECUTE _sql
INTO _sql; -- overwrite current _sql content
_sql := format('CREATE TABLE working.%I AS %s;', new_tab_name, _sql);
EXECUTE _sql;
END
$func$;
I would replace the * in the SELECT statement with the columns that you need.
info about polymorphic table functions
I'm using a polymorphic function (skip_col_not_in_model ) to remove the columns of a table which are not in another table.
CREATE OR REPLACE PACKAGE pkg_skip_col
AS
FUNCTION skip_col (tab TABLE, col COLUMNS)
RETURN TABLE
PIPELINED ROW POLYMORPHIC USING pkg_skip_col;
FUNCTION describe (tab IN OUT DBMS_TF.TABLE_T, col DBMS_TF.COLUMNS_T)
RETURN DBMS_TF.DESCRIBE_T;
FUNCTION skip_col_not_in_model (tab TABLE, tabk_model TABLE)
RETURN TABLE
PIPELINED ROW POLYMORPHIC USING pkg_skip_col;
FUNCTION describe (tab IN OUT DBMS_TF.TABLE_T,
tab_model IN DBMS_TF.TABLE_T)
RETURN DBMS_TF.DESCRIBE_T;
END;
CREATE OR REPLACE PACKAGE BODY pkg_skip_col
AS
FUNCTION describe (tab IN OUT DBMS_TF.TABLE_T,
tab_model IN DBMS_TF.TABLE_T)
RETURN DBMS_TF.DESCRIBE_T
AS
BEGIN
FOR i IN 1 .. tab.column.COUNT ()
LOOP
FOR j IN 1 .. tab_model.column.COUNT ()
LOOP
tab.column (i).PASS_THROUGH :=
tab.column (i).DESCRIPTION.NAME = tab_model.column (i).DESCRIPTION.NAME ;
EXIT WHEN tab.column (i).PASS_THROUGH;
END LOOP;
END LOOP;
RETURN NULL;
END;
FUNCTION describe (tab IN OUT DBMS_TF.TABLE_T, col DBMS_TF.COLUMNS_T)
RETURN DBMS_TF.DESCRIBE_T
AS
BEGIN
FOR i IN 1 .. tab.column.COUNT ()
LOOP
FOR j IN 1 .. col.COUNT ()
LOOP
tab.column (i).PASS_THROUGH :=
tab.column (i).DESCRIPTION.NAME != col (j);
EXIT WHEN NOT tab.column (i).PASS_THROUGH;
END LOOP;
END LOOP;
RETURN NULL;
END;
END;
/
I can compile the body. But not the spec.
[Error] Compilation (24: 59): PLS-00766: more than one parameter of TABLE type is not allowed
Similary to this query
with a (a1,a2) as (select 1,2 from dual) select * from
pkg_skip_col.skip_col(a,COLUMNS(a1))
a1:1
I would like to do that
with a (a1,a2) as (select 1,2 from dual),
b (a1,a2,a3) as (select 1,2,3 from dual)
select * from pkg_skip_col.skip_col_not_in_model(a,b)
a1:1, a2: 2 (expected resulted only)
The problem comes from the fact that I can't give two table.
to avoid this problem, I can give the table_model as a string, get the column (search in table all_tab_columns ).
But the code is not so easy to understand anymore und doesn't work with common table expression.
Has somebody ideas of improvement?
code
You can do this (from 19.6) using SQL macros. Unlike polymorphic table functions, these allow you to have many dbms_tf.table_t arguments.
SQL macros return the text of a SQL expression which is constructed at parse time. So here you can build the select list similar to the method you've used in the PTF.
For example:
create or replace function skip_col_not_in_model (
query_table dbms_tf.table_t,
ref_table dbms_tf.table_t
)
return clob sql_macro as
select_list clob;
stmt clob;
begin
<<query_cols>>
for i in 1 .. query_table.column.count loop
for j in 1 .. ref_table.column.count loop
if query_table.column(i).description.name = ref_table.column(j).description.name then
select_list :=
select_list
|| query_table.column(i).description.name
|| ',';
continue query_cols;
end if;
end loop;
end loop;
stmt := 'select '
|| rtrim ( select_list, ',' )
|| ' from query_table';
return stmt;
end skip_col_not_in_model;
/
with a (a1,a2) as (
select 1,2 from dual
), b (a1,a2,a3) as (
select 1,2,3 from dual
)
select *
from skip_col_not_in_model ( a, b );
A1 A2
---------- ----------
1 2
You can convert any describe only polymorphic table to a macro.
to avoid this problem, I can give the table_model as a string
In general this doesn't work! As the docs say:
While the constant scalar values are passed as-is to the DESCRIBE
function, all other values are passed as NULLs.
So if you use a bind variable to pass the table name, it's value is null.
In this example, notice that when using :new_col for the string parameter val its null in the describe:
create or replace package ptf_pkg
as
function select_val (tab table, val varchar2)
return table
pipelined row polymorphic using ptf_pkg;
function describe ( tab in out dbms_tf.table_t, val varchar2 )
return dbms_tf.describe_t;
end;
/
create or replace package body ptf_pkg
as
function describe ( tab in out dbms_tf.table_t, val varchar2 )
return dbms_tf.describe_t
as
begin
dbms_tf.trace ( 'val = ' || nvl ( val, 'IT IS NULL' ) );
return null;
end;
end;
/
select * from ptf_pkg.select_val ( dual, 'Y' );
D
-
X
val = Y
var new_col varchar2(30);
exec :new_col := 'NEW_COL';
select * from ptf_pkg.select_val ( dual, :new_col );
D
-
X
val = IT IS NULL
This is undoubtedly a very basic Dynamic PL/SQL question, but I am stuck.
I am looking to write a dynamic PL/SQL which will function the same as the code below, returning values of field1, field2, field3 from all rows of tablename:
SELECT field1, field2, field3 FROM databasename.tablename;
If I write the following Dynamic SQL, I can get it to successfully execute. However, I can't get it to return anything:
declare
Query VARCHAR2(200) := 'SELECT field1, field2, field3 FROM databasename.tablename';
begin
EXECUTE IMMEDIATE Query;
end;
How does one return the results of the Dynamic SQL to match the one-line SELECT statement above, please?
Your first select statement is sql query and it can return the result as an tabular format.
But your second code is anonymous block and you can not execute any select query in anonumous block without INTO clause.
If you know that your select query is going to give you single record then use the INTO clause as follows:
EXECUTE IMMEDIATE Query into var1, var2, var3;
Note: declare the variables var1, var2 and var3 in declare section with proper data type.
A straightforward method to return the dataset from a Query string for your case would be using a cursor within a Stored Function or Procedure with return type SYS_REFCURSOR such as
CREATE OR REPLACE FUNCTION Get_Result_Of_TheTable RETURN SYS_REFCURSOR IS
Query VARCHAR2(200) := 'SELECT field1, field2, field3 FROM tablename';
v_recordset SYS_REFCURSOR;
BEGIN
OPEN v_recordset FOR Query;
RETURN v_recordset;
END;
/
and then call from SQL Developer's console as
DECLARE
result SYS_REFCURSOR;
BEGIN
:result := Get_Result_Of_TheTable;
END;
/
PRINT result;
There are several possibilities. If you know which fields are returned, then you can solve it this way:
DECLARE
TYPE cursor_type IS REF CURSOR;
l_cursor cursor_type;
l_record tablename%ROWTYPE;
l_query VARCHAR2(32767) := 'SELECT field1, field2, field3 FROM tablename';
BEGIN
OPEN l_cursor FOR l_query;
LOOP
FETCH l_cursor INTO l_record;
EXIT WHEN l_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE (l_record.field1 ||' '|| l_record.field2 ||' '|| l_record.field3);
END LOOP;
CLOSE l_cursor;
END;
/
Or you can write a small pipelined function:
CREATE OR REPLACE PACKAGE pkg_test
AS
TYPE test_type IS TABLE
OF tablename%ROWTYPE;
FUNCTION get_fields
RETURN test_type PIPELINED;
END pkg_test;
/
CREATE OR REPLACE PACKAGE BODY pkg_test
AS
FUNCTION get_fields
RETURN test_type PIPELINED
AS
TYPE cursor_type IS REF CURSOR;
l_cursor cursor_type;
l_record tablename%ROWTYPE;
l_query VARCHAR2(32767) := 'SELECT field1, field2, field3 FROM tablename';
BEGIN
OPEN l_cursor FOR l_query;
LOOP
FETCH l_cursor INTO l_record;
EXIT WHEN l_cursor%NOTFOUND;
PIPE ROW (l_record);
END LOOP;
CLOSE l_cursor;
END;
END pkg_test;
/
SELECT * FROM TABLE(pkg_test.get_fields);
This was for my test:
CREATE TABLE tablename
( field1 VARCHAR2(10),
field2 VARCHAR2(10),
field3 VARCHAR2(10)
);
INSERT INTO tablename VALUES ('A1', 'A2', 'A3');
INSERT INTO tablename VALUES ('B1', 'B2', 'B3');
INSERT INTO tablename VALUES ('C1', 'C2', 'C3');
COMMIT;
You can use the bulk collect into clause, like for static SQL in PL/SQL block.
Here is a simple example:
drop table users_tb;
create table users_tb (
id integer generated always as identity,
--
username varchar2(30) not null,
is_active char(1) default 1 not null,
created_date date default sysdate not null ,
edited_date date,
--
constraint user_id_pk primary key (id),
constraint user_is_active_ch check (is_active in (1,0))
);
insert into users_tb (username) values ('john.wick');
insert into users_tb (username) values ('constantine');
insert into users_tb (username) values ('neo');
commit;
declare
-- types
type tr_list is record (
username varchar2(30),
is_active integer
);
type tt_usernamelist is table of tr_list
index by pls_integer;
-- variables
lt_users tt_usernamelist;
l_stmt varchar2(2000);
begin
-- select statement
l_stmt := 'select username, is_active from users_tb';
-- execution of dynamic code
execute immediate l_stmt
bulk collect into lt_users;
-- loop over retrived data
for i in 1..lt_users.count loop
dbms_output.put_line('User: '||lt_users(i).username||' - Is active: '||lt_users(i).is_active);
end loop;
end;
The important part is the execution of dynamic code where I am using bulk collect into. Here is a link to DBFiddle to try out.
I need fetch refcursor into temporary table. Each refcursor column should match appropriate table column + one key (enumerate) column should be in temp table. For example refcursor return below data:
'one' 'Monday'
'two' 'Friday'
And the data which should store in table:
1 'one' 'Monday'
2 'two' 'Friday'
This refcursor is opened in other functions. So I does not know what columns should be in result set.
How I can implement something like FETCH ALL curs INTO temp_table ?
I wrote below function but it throws the error for (V_CURS_Rec).*
CREATE OR REPLACE FUNCTION FN_TEST()
RETURNS VOID LANGUAGE plpgsql
AS $$
DECLARE
V_CURS REFCURSOR;
V_CURS_Rec RECORD;
ITER INTEGER;
BEGIN
create temporary table if not exists TMP_TBL
(
INDX INTEGER NOT NULL,
CNAME VARCHAR(20),
CDAY VARCHAR(20),
);
DELETE FROM TMP_TBL;
SELECT * FROM FN_RET_REFCURSOR() INTO V_CURS;
ITER := 1;
LOOP
FETCH V_CURS INTO V_CURS_Rec;
EXIT WHEN NOT FOUND;
INSERT INTO TMP_TBL SELECT ITER, (V_CURS_Rec).*;
ITER := ITER + 1;
END LOOP;
RETURN;
END; $$;
As a workaround I have done below
CREATE OR REPLACE FUNCTION FN_TEST()
RETURNS VOID LANGUAGE plpgsql
AS $$
DECLARE
V_CURS REFCURSOR;
V_Rec_CNAME VARCHAR(20);
V_Rec_CDAY VARCHAR(20);
ITER INTEGER;
BEGIN
create temporary table if not exists TMP_TBL
(
INDX INTEGER NOT NULL,
CNAME VARCHAR(20),
CDAY VARCHAR(20)
);
DELETE FROM TMP_TBL;
SELECT * FROM FN_RET_REFCURSOR() INTO V_CURS;
ITER := 1;
LOOP
FETCH V_CURS INTO V_Rec_CNAME, V_Rec_CDAY;
EXIT WHEN NOT FOUND;
INSERT INTO TMP_TBL VALUES (ITER, V_Rec_CNAME, V_Rec_CDAY);
ITER := ITER + 1;
END LOOP;
RETURN;
END; $$;
how to create oracle view by function
1. First create a type 'p_type'
2. Create a table 'p_table' as 'p_type'
3. A function with 'p_table' pipeline
create or replace type p_type as object (
v_name varchar(400),
d_date date,
i_val number
);
create or replace type p_table as table of p_type;
create or replace function get_info return p_table pipelined
is
c number;
begin
c := 1;
/*
A is table and it has only one 'a' attribute
*/
for i in (select * from A) loop
pipe row( p_type(
v_name => SUBSTR(i.A, 1, 10),
d_date => sysdate,
i_val => c
) );
c := c + 1;
end loop;
return;
end;
CREATE VIEW vw_info AS
select * from table(get_info);