I'm trying to print using a table of records. The original table looks as follows:
CREATE TABLE dept1
( DEPTNO NUMBER(3) PRIMARY KEY,
DNAME VARCHAR2(20),
LOC VARCHAR2(20)
);
Dept = {(10, 'ACCOUNTING', 'NEW YORK'),
(20, 'RESEARCH', 'DALLAS'),
(30, 'SALES', 'CHICAGO'),
(40, 'OPERATIONS', 'DALLAS'),
(50, 'MARKETING', 'BOSTON')}
What I think I'm doing with this code is creating a procedure print_deptlist that will print all the records from the record deptlist that I created: We're required to use that method
CREATE OR REPLACE PROCEDURE print_deptlist IS
deptlist dept1%rowtype;
Begin
Select * into deptlist from dept1;
DBMS_OUTPUT.PUT_LINE(DEPTLIST.deptno || ',' || deptlist.dname || ',' || deptlist.loc);
end;
/
See above...
I'm expecting results that looks lists the column names with the records below:
You can use such a loop statement through
BULK COLLECT which is SELECT statements that retrieve multiple rows with a single fetch, also improving the speed of data retrieval
SQL> SET SERVEROUTPUT ON
SQL> CREATE OR REPLACE PROCEDURE print_deptlist IS
TYPE tab_dept IS TABLE OF dept1%rowtype;
deptlist tab_dept;
BEGIN
SELECT * BULK COLLECT INTO deptlist FROM dept1;
FOR i IN 1..deptlist.count
LOOP
dbms_output.put_line(deptlist(i).deptno || ',' || deptlist(i).dname
|| ',' || deptlist(i).loc);
END LOOP;
END;
/
Demo
Related
I know I didn't make it clear in the question title. Let me explain.
Say I have a Table SOURCE_TABLE with 1 column that looks like this:
Filter
------------------|
Name='John'
Surname = 'Smith'
Age = '25'
And I want to use this table as a filter. Like below:
SELECT * FROM TARGET_TABLE WHERE (SELECT FILTER FROM SOURCE_TABLE)
I heard maybe evaluate function could help me but to be honest I couldn't understand how.
Do you know any method that I can use a column as my filter source like above?
Edit1:
DECLARE
my_filter VARCHAR2(100);
my_query VARCHAR2(500);
BEGIN
my_query := 'SELECT FILTER FROM SOURCE_TABLE WHERE ROWNUM=1';
EXECUTE IMMEDIATE my_query INTO my_filter;
EXECUTE IMMEDIATE 'SELECT * FROM TARGET_TABLE WHERE '|| my_filter;
END;
#Sujitmohanty30 I have come up with the above after learning EXECUTE IMMEDIATE. But there is a problem I stumble upon. This needs to be dynamic regarding to the end result and i want to see the result of the select query at the end.
Let's say we have these 2 tables:
create table TARGET_TABLE(name varchar2(30), surname varchar2(30));
insert into TARGET_TABLE(name,surname) values ('John', 'Doe');
insert into TARGET_TABLE(name,surname) values ('Ann', 'Smith');
insert into TARGET_TABLE(name,surname) values ('Steven', 'Feuerstein');
insert into TARGET_TABLE(name,surname) values ('Steven', 'King');
commit;
create table SOURCE_TABLE(filter) as
select q'[name='John']' from dual union all
select q'[name='Ann']' from dual union all
select q'[name='Steven' and surname='King']' from dual union all
select q'[surname='Feuerstein']' from dual;
Then you can use xmltable and dbms_xmlgen.getXMLtype to get such rows dynamically:
https://dbfiddle.uk/?rdbms=oracle_18&fiddle=72abdf18b149cf30882cb4e1736c9c33
select *
from SOURCE_TABLE
, xmltable(
'/ROWSET/ROW'
passing dbms_xmlgen.getXMLtype('select * from TARGET_TABLE where '|| SOURCE_TABLE.filter)
columns
name varchar2(30) path 'NAME',
surname varchar2(30) path 'SURNAME'
) x;
Results:
FILTER NAME SURNAME
-------------------------------- ------------------------------ ------------------------------
name='John' John Doe
name='Ann' Ann Smith
name='Steven' and surname='King' Steven King
surname='Feuerstein' Steven Feuerstein
I have learnt how to return a dynamically opened ref cursor. Now based on the output, I want to create a table or a view.
Input table:
create table sales
(s_sale_name varchar2(20),
s_date_sal date,
s_qty number(10)
);
Records
insert into sales values ('Norb','10-MAR-2019',10000);
insert into sales values ('Bert','10-MAR-2019',5000);
insert into sales values ('Alba','10-MAR-2019',4000);
insert into sales values ('Rob','10-MAR-2019',200000);
insert into sales values ('Norb','11-MAR-2019',5000);
insert into sales values ('Bert','11-MAR-2019',13000);
insert into sales values ('Rob','11-MAR-2019',80000);
insert into sales values ('Norb','12-MAR-2019',1000);
insert into sales values ('Bert','12-MAR-2019',4000);
insert into sales values ('Rob','12-MAR-2019',40000);
insert into sales values ('Alba','12-MAR-2019',2000);
Query output
sales_name 10-MAR-2019 11-MAR-2019 12-MAR-2019
Norb 10000 5000 1000
Bert 5000 13000 4000
Alba 4000 0 2000
Rob 200000 80000 40000
Now the result should be saved in a table or a view. I have learnt how to return a dynamically opened ref cursor so far.
------Here is the procedure I used -----------
create or replace package p_sales_pkg
as
type rc is ref cursor;
procedure get_query( p_cursor in out rc, p_start date, p_end date );
end;
/
create or replace package body p_sales_pkg
as
procedure get_query( p_cursor in out rc, p_start date, p_end date )
is
l_query long := 'select s_name ';
begin
for i in 1 .. trunc(p_end)-trunc(p_start)+1
loop
l_query := l_query || ', sum( decode( trunc(s_date), ' ||
'to_date( ''' || to_char(p_start+i-1,'yyyymmdd') ||
''', ''yyyymmdd'' ), s_qty, 0 )) "' ||
to_char(p_start+i-1) || '"';
end loop;
l_query := l_query || ' from sales group by s_name';
open p_cursor for l_query;
end;
end;
/
set autoprint on
var x refcursor
exec nw_demo_pkg.get_query( :x, '10-MAR-19', '13-MAR-19' );
This is really a very nice and challenging question. I disagree on the #APC point on SELECT part of a CREATE TABLE ... AS SELECT statement. Well we definitely can't do that. What i believe is to every problem in Oracle, there exists a solution.
You requirement can be achieved using a NESTED TABLE. See below:
Set Up:
create table sales
(s_sale_name varchar2(20),
s_date_sal date,
s_qty number(10)
);
/
insert into sales values ('Norb','10-MAR-2019',10000);
insert into sales values ('Bert','10-MAR-2019',5000);
insert into sales values ('Alba','10-MAR-2019',4000);
insert into sales values ('Rob','10-MAR-2019',200000);
insert into sales values ('Norb','11-MAR-2019',5000);
insert into sales values ('Bert','11-MAR-2019',13000);
insert into sales values ('Rob','11-MAR-2019',80000);
insert into sales values ('Norb','12-MAR-2019',1000);
insert into sales values ('Bert','12-MAR-2019',4000);
insert into sales values ('Rob','12-MAR-2019',40000);
insert into sales values ('Alba','12-MAR-2019',2000);
---Created an Object of Sales table to hold intermediate result
create or replace type sales_obj is OBJECT
(obj_sale_name varchar2(20),
obj_date_sal date,
obj_qty number(10)
);
/
-- Table of Sales Object.
create or replace type vtest1Tab is table of sales_obj;
/
Anonymous Block to Create table ccc:
DECLARE
VAR VTEST1TAB ;
vsql varchar2(500);
BEGIN
vsql := 'create table ccc(col1) NESTED TABLE COL1 STORE AS TAB1
as
Select cast(multiset(Select * from SALES) as VTEST1TAB )
from dual
';
Execute immediate vsql ;
END;
Output:
SQL> Select p.*
from ccc c,
table(c.COL1) p ;
In this link , The reply by "Zlatko Sirotic" covers exactly how to identify columns of the cursor and print them.
Look for "dyn_fetch", as the package is generic enough, it can work with any query for printing data. You can use the same approach to insert the data into a table that is created dynamically.
I'm working on an Oracle Stored procedure.
I need to iterate over rows of a table . I can do that using:
FOR eachrow IN table_name
LOOP
END LOOP;
But i need the table_name to be dynamic.
For example the table names are stored in some other table.
So, i can do FOR loop on that table and inside the loop ,i want to iterate through the rows of the new table.
Please suggest how i can achieve that.
Thanks,
Sash
This is not possible with a FOR eachrow IN table_name LOOP, you have to use a ref cursor instead:
First of all some sample data. These tables have some column names in common, in your inner for loop you can only access these columns, this is what I wrote in my comment above. Result type should be the same in the inner loop.
create table froc_a(id number, name varchar2(10), row_added date);
insert into froc_a values (1, '1', sysdate);
insert into froc_a values (2, '2', sysdate - 2);
insert into froc_a values (4, '4', sysdate - 4);
create table froc_b(id number, name2 varchar2(10), row_added date);
insert into froc_b values (1, 'b1', sysdate);
insert into froc_b values (2, 'b2', sysdate - 2);
insert into froc_b values (4, 'b4', sysdate - 4);
create table froc_c(id number, txt varchar2(10), row_added date);
insert into froc_c values (1, 'c1', sysdate);
insert into froc_c values (2, 'c2', sysdate - 2);
insert into froc_c values (4, 'c4', sysdate - 4);
Here is a first approach how to write it:
declare
TYPE curtype IS REF CURSOR;
l_cursor curtype;
l_param_id number;
l_id number;
l_val varchar2(100);
begin
l_param_id := 1;
-- Loop over your table names
for l_rec in (with tabnames(name) as
(select 'froc_a'
from dual
union all
select 'froc_b'
from dual
union all
select 'froc_c'
from dual)
select * from tabnames) loop
dbms_output.put_line(l_rec.name);
-- Open cursor for current table
open l_cursor for 'select id, row_added from ' || l_rec.name || ' where id = :1'
using l_param_id;
-- Loop over rows of current table
loop
fetch l_cursor
into l_id, l_val;
exit when l_cursor%notfound;
dbms_output.put_line(l_id || ', ' || l_val);
end loop;
end loop;
end;
Output:
froc_a
1, 06-APR-16
froc_b
1, 06-APR-16
froc_c
1, 06-APR-16
I am having trouble merging a table with a collection.
Let's say I have a table emp.
Here is my PL/SQL code snippet.
TYPE empcol is table of emp%ROWTYPE INDEX BY BINARY_INTEGER;
tmpemp empcol;
-- Code here to load data from a CSV file into tmpemp collection
-- tmpemp(1).emp_id := parsedstring
-- etc.
MERGE INTO emp A using tmpemp B ON A.emp_id = B.emp_id
WHEN MATCHED THEN UPDATE SET A.fname = B.fname, A.lname = B.lname
WHEN NOT MATCHED THEN INSERT (emp_id, fname, lname) VALUES (b.emp_id, b.fname, b.lname);
Compiler doesn't like it. Its throwing ORA-0942 - Table or View doesn't exist.
What am I doing wrong? or How can I do this better.
Thanks a lot for any help you can provide.
PL/SQL types like emp%ROWTYPE or TABLE OF ... INDEX BY ... cannot be used in SQL queries.
The type must be declared as SQL type (not as PL/SQL type) to be used in SQL query.
Try this approach (example):
create table emp(
firstname varchar2(100),
salary number
);
insert into emp values( 'John', 100 );
commit;
create type my_emp_obj is object(
firstname varchar2(100),
salary number
);
/
create type my_emp_obj_table is table of my_emp_obj;
/
declare
my_emp_tab my_emp_obj_table;
begin
null;
my_emp_tab := my_emp_obj_table( my_emp_obj( 'John', 200 ), my_emp_obj( 'Tom', 300 ));
MERGE INTO emp
USING ( SELECT * FROM TABLE( my_emp_tab )) src
ON ( emp.firstname = src.firstname )
WHEN MATCHED THEN UPDATE SET salary = src.salary
WHEN NOT MATCHED THEN INSERT VALUES( src.firstname, src.salary );
end;
/
select * from emp;
FIRSTNAME SALARY
----------------------- ----------
John 200
Tom 300
I have a generic datamodel with 3 tables
CREATE TABLE Properties
(
propertyId int(11) NOT NULL AUTO_INCREMENT,
name varchar(80) NOT NULL
)
CREATE TABLE Customers
(
customerId int(11) NOT NULL AUTO_INCREMENT,
customerName varchar(80) NOT NULL
)
CREATE TABLE PropertyValues
(
propertyId int(11) NOT NULL,
customerId int(11) NOT NULL,
value varchar(80) NOT NULL
)
INSERT INTO Properties VALUES (1, 'Age');
INSERT INTO Properties VALUES (2, 'Weight');
INSERT INTO Customers VALUES (1, 'Bob');
INSERT INTO Customers VALUES (2, 'Tom');
INSERT INTO PropertyValues VALUES (1, 1, '34');
INSERT INTO PropertyValues VALUES (2, 1, '80KG');
INSERT INTO PropertyValues VALUES (1, 2, '24');
INSERT INTO PropertyValues VALUES (2, 2, '53KG');
What I would like to do is create a view that has as columns all the ROWS in Properties and has as rows the entries in Customers. The column values are populated from PropertyValues.
e.g.
customerId Age Weight
1 34 80KG
2 24 53KG
I'm thinking I need a stored procedure to do this and perhaps a materialised view (the entries in the table "Properties" change rarely).
Any tips?
It's easy enough to generate a view with dynamic SQL:
create or replace procedure gen_view
as
cols_stmt varchar2(32767);
from_stmt varchar2(32767);
subq_name varchar2(30);
begin
for r in ( select * from properties
order by propertyid )
loop
subq_name := 'pv_'||trim(to_char(r.propertyid));
cols_stmt := cols_stmt || ', '|| subq_name ||'.value as '||r.name;
from_stmt := from_stmt || ' left join ( select value, customerid from propertyvalues where propertyid = '
||trim(to_char(r.propertyid))||') '||subq_name
||' on '||subq_name||'.customerid = customers.customerid';
end loop;
execute immediate 'create or replace view eav_view as select customers.customerid, customers.customername'
|| cols_stmt
|| ' from customers '
|| from_stmt;
end gen_view;
/
Here's it working:
SQL> exec gen_view
PL/SQL procedure successfully completed.
SQL> select * from eav_view
2 /
CUSTOMERID
----------
CUSTOMERNAME
--------------------------------------------------------------------------------
AGE
--------------------------------------------------------------------------------
WEIGHT
--------------------------------------------------------------------------------
1
Bob
34
80KG
2
Tom
24
53KG
SQL>
Let's create a new property and insert values for it for some of the customers...
SQL> insert into properties values (3, 'FavouriteIceCream')
2 /
1 row created.
SQL> insert into propertyvalues values (3, 1, 'Cherry Garcia')
2 /
1 row created.
SQL> exec gen_view
PL/SQL procedure successfully completed.
SQL> select * from eav_view
2 /
CUSTOMERID
----------
CUSTOMERNAME
--------------------------------------------------------------------------------
AGE
--------------------------------------------------------------------------------
WEIGHT
--------------------------------------------------------------------------------
FAVOURITEICECREAM
--------------------------------------------------------------------------------
1
Bob
34
80KG
Cherry Garcia
2
Tom
24
53KG
SQL>
"I'm thinking I need a stored
procedure to do this and perhaps a
materialised view (the entries in the
table "Properties" change rarely)."
The problem is, Properties are going to change, and I'm guessing you will have no oversight of when that happens. So you are going to find it very hard to apply the changes to a materialized view. This matters because changing the projection of a materialized view necessitates dropping it. So it's quite difficult to do this without an interruption to service. A similar consideration applies to the regular view , but the outage is almost zero.
If you do want to convert the view statement into a materialized view note that Oracle doesn't seem to like the ANSI-92 syntax when it comes to materialized views (it hurls ORA-12054). I'm not sure why that should be, but the problem went away when I changed to the older joining technique, which is annoying because the outer join syntax is clunkier.
A solution without the need to re-create database objects would be to use the dynamic SQL in a function which returns a Ref Cursor, which maps to a JDBC ResultSet:
create or replace function get_eav_view
return sys_refcursor
as
cols_stmt varchar2(32767);
from_stmt varchar2(32767);
subq_name varchar2(30);
return_value sys_refcursor;
begin
for r in ( select * from properties
order by propertyid )
loop
subq_name := 'pv_'||trim(to_char(r.propertyid));
cols_stmt := cols_stmt || ','|| subq_name ||'.value as '||r.name;
from_stmt := from_stmt || ' left join ( select value, customerid from propertyvalues where propertyid = '
||trim(to_char(r.propertyid))||') '||subq_name
||' on '||subq_name||'.customerid = customers.customerid';
end loop;
open return_value for
'select customers.customerid, customers.customername'
|| cols_stmt
|| ' from customers '
|| from_stmt;
return return_value;
end get_eav_view;
/
This will always return the latest projection:
SQL> var rc refcursor
SQL> exec :rc := get_eav_view
PL/SQL procedure successfully completed.
SQL> print rc
CUSTOMERID
----------
CUSTOMERNAME
--------------------------------------------------------------------------------
AGE
--------------------------------------------------------------------------------
WEIGHT
--------------------------------------------------------------------------------
FAVOURITEICECREAM
--------------------------------------------------------------------------------
1
Bob
34
80KG
Cherry Garcia
2
Tom
24
53KG
SQL>
Now, if we add a new property it gets picked up immediately:
SQL> insert into properties values (4, 'StarSign')
2 /
1 row created.
SQL> insert into propertyvalues values (4, 2, 'Aries')
2 /
1 row created.
SQL> exec :rc := get_eav_view
PL/SQL procedure successfully completed.
SQL> print rc
CUSTOMERID
----------
CUSTOMERNAME
--------------------------------------------------------------------------------
AGE
--------------------------------------------------------------------------------
WEIGHT
--------------------------------------------------------------------------------
FAVOURITEICECREAM
--------------------------------------------------------------------------------
STARSIGN
--------------------------------------------------------------------------------
1
Bob
34
80KG
Cherry Garcia
2
Tom
24
53KG
Aries
SQL>