I have this tables:
CREATE TABLE departments (
dep_na number(2) NOT NULL PRIMARY KEY,
dname VARCHAR2(15),
loc VARCHAR2(15)
);
INSERT INTO departments VALUES (20,'CONTABILITY','SEVILLA');
INSERT INTO departments VALUES (30,'INVEST','MADRID');
COMMIT;
CREATE TABLE employees (
emp_nu number(4) NOT NULL PRIMARY KEY,
surname VARCHAR2(10),
oficio VARCHAR2(10),
dir number(4),
date_a DATE ,
salar number(6,2),
comis number(6,2),
dep_na number(2) NOT NULL REFERENCES departments(dept_no)
);
ALTER SESSION SET NLS_DATE_FORMAT='DD/MM/YYYY';
INSERT INTO employees VALUES (7369,'SANCHEZ','EMPLEADO',7902,'17/12/1990',1040,NULL,20);
INSERT INTO employees VALUES (7499,'ARROYO','VENDEDOR',7698,'20/02/1990',1500,390,30);
COMMIT;
create or replace
TYPE TDEP AS OBJECT(
dep_na NUMBER(2),
dname VARCHAR2(15),
loc VARCHAR2(15)
);
CREATE OR REPLACE TYPE TEMPLE AS OBJECT(
emp_nu number(4),
surname VARCHAR2(10),
oficio VARCHAR2(10),
dir number(4),
date_a DATE,
salar number(6,2),
comision number(6,2),
dep_na TDEP
);
CREATE OR REPLACE TYPE VEMPLE AS VARRAY(20) OF TEMPLE;
I created the following table pack with varray and types, but I have the problem that when I insert the contents of the employees and departments tables in the table pack the computer gives me error.
CREATE TABLE pack(
array_employees VEMPLE,
departme TDEP
);
I have problems with this code:
DECLARE
T VEMPLE;
A TDEP
CURSOR C1 is select * from departamentos order by dep_na;
CURSOR C2(DEPAR NUMBER) is select * from empleados where dep_na = depar;
j integer := 1;
BEGIN
for i in C1 LOOP
DBMS_OUTPUT.PUT_LINE(i.dep_na);
T := NEW VEMPLE();
A := NEW TDEP();
j := 1;
for x in C2(i.dep_na) loop
if j < T.LIMIT THEN
DBMS_OUTPUT.PUT_LINE(x.apellido || ' - ' || i.dep_na);
T.extend;
T(j) := NEW TEMPLE(NULL, NULL, NULL, NULL);
T(j).departments := NEW TDEP(i.loc, NULL, NULL);
j := j + 1;
end if;
end loop;
INSERT INTO Grupos VALUES(i.A, T);
end loop;
end;
/
I need help with this query in Oracle. I have problems with the cursor.
The immediate problem is that you're just missing a semicolon after A TDEP in the declare section, which is causing your ORA-06550: line 4, column 1: PLS-00103: Encountered the symbol "CURSOR" when expecting ... error. Which you haven't shown, but you said it's a cursor error, so that seems to line up.
But you have lots of other errors, some from inconsistent naming which I think is from partially changing things for posting, but partly from how you're creating object instances. I think this is pretty much clean:
DECLARE
T VEMPLE;
A TDEP;
CURSOR C1 is select * from departments order by dep_na;
CURSOR C2(DEPAR NUMBER) is select * from employees where dep_na = depar;
j integer := 1;
BEGIN
for i in C1 LOOP
DBMS_OUTPUT.PUT_LINE(i.dep_na);
T := NEW VEMPLE();
-- supply values to constructor
A := NEW TDEP(i.dep_na, i.dname, i.loc);
j := 1;
for x in C2(i.dep_na) loop
if j < T.LIMIT THEN
DBMS_OUTPUT.PUT_LINE(x.surname|| ' - ' || i.dep_na);
T.extend;
-- supply values to constructor
T(j) := NEW TEMPLE(x.emp_nu, x.surname, x.oficio,
x.dir, x.date_a, x.salar, x.comis, A);
-- no idea what the next line is doing
-- T(j).departments := NEW TDEP(i.loc, NULL, NULL);
j := j + 1;
end if;
end loop;
-- elements were wrong way round, which wouldn't matter if you
-- included the column names - which is good practice anyway
INSERT INTO pack VALUES(T, A);
end loop;
end;
/
SQL Fiddle. (Don't try to select from the table in the Fiddle though, it struggles with object types; this just shows it doesn't error...)
I assume this is an exercise. There are probably better and more efficient ways to convert from a relational to an object schema.
Related
Only getting my head around cursor loops and the likes lately, so might be something very simple with my code that's causing the problem
I am using a cursor to spool through customer data to create an xml file. It needs to be sorted by date so that the most recent data is at the bottom of the xml file.
when I run the sql for the cursor, i can see the data is ordered by date. But when I run the entire procedure and check the output, it seems to be ordered by date but on closer inspection some of the records are not in the correct order.
here is the code I'm running. I've omitted a lot of the query as its just xml padding, but I don't think that should make a difference.
the output is written to a table, which i then copy and paste into notepad++. When checking the output table I can see that the order is wrong
drop table recs_xml_output;
create table recs_xml_output (XML_STRING VARCHAR2 (4000 char));
declare
PROCEDURE p_generate_ohmpi_record
IS
lv_string VARCHAR2(10000 CHAR) := NULL;
lv_date_format VARCHAR2(20 CHAR) := 'YYYY-MM-DD';
lv_time_format VARCHAR2(20 CHAR) := 'HH24:MI:SS';
n_id PLS_INTEGER := NULL;
CURSOR c_patient_xml IS
select *
from sbyn_transaction T
where timestamp >= '07-JAN-22 11.58.02.139977000'
and timestamp <= '07-JAN-22 17.51.26.054240000'
ORDER BY TIMESTAMP;
begin
for v_patient_xml in c_patient_xml
loop
lv_string := n_id||'<Person><SourceID>';
lv_string := lv_string||v_patient_xml.lid||'</SourceID><PPSN>'||v_patient_xml.lid||'</PPSN>';
lv_string := lv_string||'<PPSNLastUpdated>';
lv_string := lv_string||TO_CHAR( v_patient_xml.pps_number_updated,lv_date_format )||'T'||TO_CHAR( v_patient_xml.pps_number_updated,lv_time_format)||'</PPSNLastUpdated>';
lv_string := lv_string||'<Birth>';
IF v_patient_xml.date_of_birth IS NOT NULL THEN
lv_string := lv_string||'<DateOfBirth>'||TO_CHAR( v_patient_xml.date_of_birth,lv_date_format )||'T'||TO_CHAR( v_patient_xml.date_of_birth,lv_time_format)||'</DateOfBirth>';
else lv_string := lv_string||'<DateOfBirth></DateOfBirth>';
END IF;
...
insert into recs_xml_output VALUES (lv_string);
END LOOP;
COMMIT;
end p_generate_ohmpi_record;
begin
p_generate_ohmpi_record;
end;
/
The main issue with your code is that you aren't storing the ordering column in your output table, and you're relying on the rows being returned from that table in the order they were inserted.
Unfortunately, as it's a heap table, the order of insertion is not necessarily going to be the same as the order you retrieve them. In order to guarantee a specific ordering of the rows when selecting from a table, you need to have an order by clause.
Therefore you could do something like:
create table recs_xml_output (tstamp timestamp, XML_STRING VARCHAR2 (4000 char));
PROCEDURE p_generate_ohmpi_record
IS
...
CURSOR c_patient_xml IS
select *
from sbyn_transaction T
where timestamp >= '07-JAN-22 11.58.02.139977000'
and timestamp <= '07-JAN-22 17.51.26.054240000'
ORDER BY TIMESTAMP;
begin
for v_patient_xml in c_patient_xml
loop
...
insert into recs_xml_output (tstamp, xml_string)
VALUES (v_patient_xml.timestamp, lv_string);
END LOOP;
COMMIT;
end p_generate_ohmpi_record;
select *
from recs_xml_output
order by tstamp;
However, if your ultimate goal is simply to take your rows and output them as XML, you can do it in a single SQL statement:
WITH sbyn_transaction AS (SELECT 1 lid,
to_timestamp('11/01/2022 11:25:57.136468', 'dd/mm/yyyy hh24:mi:ss.ff6') pps_number_updated,
to_date('01/01/2000', 'dd/mm/yyyy') date_of_birth,
'info 1' info_column
FROM dual
UNION ALL
SELECT 2 lid,
to_timestamp('11/01/2022 11:23:46.115329', 'dd/mm/yyyy hh24:mi:ss.ff6') pps_number_updated,
to_date('06/10/1979', 'dd/mm/yyyy') date_of_birth,
'info 2' info_column
FROM dual
UNION ALL
SELECT 3 lid,
to_timestamp('11/01/2022 11:24:08.951232', 'dd/mm/yyyy hh24:mi:ss.ff6') pps_number_updated,
NULL date_of_birth,
'info 3' info_column
FROM dual
UNION ALL
SELECT 4 lid,
to_timestamp('11/01/2022 11:23:17.468329', 'dd/mm/yyyy hh24:mi:ss.ff6') pps_number_updated,
to_date('29/03/1957', 'dd/mm/yyyy') date_of_birth,
'info 4' info_column
FROM dual)
-- end of mimicking your table with data in it; main query below:
SELECT st.*,
XMLELEMENT("Person",
XMLFOREST(lid AS "SourceID",
lid AS "PPSN",
to_char(pps_number_updated, 'yyyy-mm-dd"T"hh24:mi:ss') AS "PPSNLastUpdated"),
XMLELEMENT("Birth",
XMLFOREST(to_char(date_of_birth, 'yyyy-mm-dd"T"hh24:mi:ss') AS "DateOfBirth") AS "Birth"),
XMLFOREST(info_column AS "SomeData")).getclobval() xml_record
FROM sbyn_transaction st
ORDER BY pps_number_updated;
LID PPS_NUMBER_UPDATED DATE_OF_BIRTH INFO_COLUMN XML_RECORD
---------- ------------------------------------------------- ------------- ----------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4 11-JAN-22 11.23.17.468329000 29/03/1957 info 4 <Person><SourceID>4</SourceID><PPSN>4</PPSN><PPSNLastUpdated>2022-01-11T11:23:17</PPSNLastUpdated><Birth><DateOfBirth>1957-03-29T00:00:00</DateOfBirth></Birth><SomeData>info 4</SomeData></Person>
2 11-JAN-22 11.23.46.115329000 06/10/1979 info 2 <Person><SourceID>2</SourceID><PPSN>2</PPSN><PPSNLastUpdated>2022-01-11T11:23:46</PPSNLastUpdated><Birth><DateOfBirth>1979-10-06T00:00:00</DateOfBirth></Birth><SomeData>info 2</SomeData></Person>
3 11-JAN-22 11.24.08.951232000 info 3 <Person><SourceID>3</SourceID><PPSN>3</PPSN><PPSNLastUpdated>2022-01-11T11:24:08</PPSNLastUpdated><Birth></Birth><SomeData>info 3</SomeData></Person>
1 11-JAN-22 11.25.57.136468000 01/01/2000 info 1 <Person><SourceID>1</SourceID><PPSN>1</PPSN><PPSNLastUpdated>2022-01-11T11:25:57</PPSNLastUpdated><Birth><DateOfBirth>2000-01-01T00:00:00</DateOfBirth></Birth><SomeData>info 1</SomeData></Person>
My code:
create table info(str varchar2(30));
declare
cursor c(job emp_ast.job_id%type, dep emp_ast.department_id%type) is select employee_id
from emp_ast
where job_id=job and department_id=dep;
type t_job is table of emp_ast.job_id%type;
t t_job:=t_job();
emp emp_ast.employee_id%type;
i number(3);
begin
select job_id
bulk collect into t
from emp_ast;
for i in 10..270 loop
for j in 1..t.count loop
open c(i, t(j));
loop
fetch c into emp;
insert into info
values (i||' '||t(j)||' '||emp);
exit when c%notfound;
end loop;
i:=i+10;
end loop;
end loop;
end;
/
I get "expression 'I' cannot be used as an assignment target", reffering to the line where I increment i by 10. I am trying to save the department_id, employee_id and job_id as a string in a table for each department and each job.
At the point where you get that message, i refers to the loop control variable i defined in the line for i in 10..270 loop, not the int(3) variable defined earlier. In PL/SQL a loop definition defines a variable which is only accessible inside the loop, and which you cannot alter. I suggest you change the name of one or the other to make them unique.
EDIT
PL/SQL doesn't provide a way to step by more than 1 in a computed FOR loop. Instead, you will need to compute the desired department number value within the loop:
DECLARE
CURSOR c(job EMP_AST.JOB_ID%TYPE,
dep EMP_AST.DEPARTMENT_ID%TYPE)
IS SELECT EMPLOYEE_ID
FROM EMP_AST
WHERE JOB_ID = job AND
DEPARTMENT_ID = dep;
TYPE t_job IS TABLE OF EMP_AST.JOB_ID%TYPE;
t t_job := t_job();
emp EMP_AST.EMPLOYEE_ID%TYPE;
nDepartment NUMBER;
BEGIN
SELECT job_id
BULK COLLECT INTO t
FROM EMP_AST;
FOR i IN 1..27 LOOP
nDepartment := i * 10;
FOR j IN 1..t.COUNT LOOP
OPEN c(t(j), nDepartment);
LOOP
FETCH c INTO emp;
INSERT INTO info
VALUES (nDepartment || ' ' || t(j) || ' ' || emp);
EXIT WHEN c%notfound;
END LOOP; -- cursor c
CLOSE c;
END LOOP; -- j
END LOOP; -- i
END;
/
Note that in the code above the nDepartment value is computed within the i loop, which now increments from 1 to 27 instead of going from 10 to 270.
Declare
Vquery varchar2(32000);
Vitem varchar2(50);
Vskuloc varchar2(50);
vstartdate Date;
Vdur Number;
vtype Number;
vqty Float(126);
GP_ohpost Date:= fnc_ohpost;
sdate1 Date:= to_date('01/01/1970','dd/mm/yyyy');
Cursor C_DRIVER is
(Select h.*,b.item,b.skuloc,h.rowid
FROM SCPOMGR.histwide h, SCPOMGR.dfutosku b
WHERE h.dmdunit=b.dmdunit
AND h.loc=b.dfuloc
AND (b.eff = Sdate1 OR b.eff <= h.startdate
AND b.disc = Sdate1 OR b.disc > h.startdate)
And NOT EXISTS (SELECT 1 FROM SCPOMGR.SKUHIST d
WHERE b.dmdunit = d.item
AND b.skuloc = d.loc
AND h.startdate = d.startdate
)) order by h.StartDate;
TYPE GP_cursor_Type IS TABLE OF C_DRIVER%ROWTYPE;
GP_cursor_tab GP_cursor_Type := GP_cursor_type();
c_limit constant PLS_INTEGER DEFAULT 10;
TYPE GP_Insert_type IS TABLE OF scpomgr.skuhist%ROWTYPE;
GP_Insert_tab GP_Insert_type := GP_Insert_type();
GP_tot_accept_fetched NUMBER := 0;
begin
OPEN C_DRIVER;
LOOP
FETCH c_driver BULK COLLECT INTO GP_cursor_tab limit c_limit;
Exit when c_driver%NOTFOUND;
FOR i IN GP_cursor_tab.FIRST .. GP_cursor_tab.LAST LOOP
vquery:= 'Select ...<skipped to make post shortest>';
Execute immediate vquery BULK COLLECT INTO GP_Insert_tab;
FORALL i IN INDICES OF GP_Insert_tab
Insert into scpomgr.skuhist
values( GP_Insert_tab(i).startdate
,1
,10080
,GP_Insert_tab(i).qty
,GP_Insert_tab(i).item
,GP_Insert_tab(i).loc
);
End Loop;
End Loop;
Close C_DRIVER;
END;
/
What i want to do here is i want to use the forall query outside of loop but if i am using the forall with the 2 nd array outside the loop then.all of the records are not getting inserted to the final table..please suggest me some solution....
Ankita.
I didn't know the structure of your tables, so i made a simple tables to understand what should work here:
create table histwide (f1 number);
insert into histwide values (1);
insert into histwide values (2);
create table skuhist (f1 number);
select * from histwide
select * from skuhist
Now i changed your code keeping changed code in comments. Seems it works. I think the trouble was because of you are used Exit when c_driver%NOTFOUND; in begining of loop. It is wrong for FORALL because after first fetching cursor is empty (if limit is reached).
So, my solution of your case:
Declare
Vquery varchar2(32000);
Vitem varchar2(50);
Vskuloc varchar2(50);
vstartdate Date;
Vdur Number;
vtype Number;
vqty Float(126);
GP_ohpost Date:= trunc(sysdate); --fnc_ohpost;
sdate1 Date:= to_date('01/01/1970','dd/mm/yyyy');
Cursor C_DRIVER is
(Select h.*--,b.item,b.skuloc,h.rowid
--FROM SCPOMGR.histwide h, SCPOMGR.dfutosku b
FROM histwide h
/*WHERE h.dmdunit=b.dmdunit
AND h.loc=b.dfuloc
AND (b.eff = Sdate1 OR b.eff <= h.startdate
AND b.disc = Sdate1 OR b.disc > h.startdate)
And NOT EXISTS (SELECT 1 FROM SCPOMGR.SKUHIST d
WHERE b.dmdunit = d.item
AND b.skuloc = d.loc
AND h.startdate = d.startdate
)) order by h.StartDate*/
);
TYPE GP_cursor_Type IS TABLE OF C_DRIVER%ROWTYPE;
GP_cursor_tab GP_cursor_Type := GP_cursor_type();
c_limit constant PLS_INTEGER DEFAULT 10;
TYPE GP_Insert_type IS TABLE OF skuhist%ROWTYPE; --scpomgr.skuhist%ROWTYPE;
GP_Insert_tab GP_Insert_type := GP_Insert_type();
GP_tot_accept_fetched NUMBER := 0;
begin
OPEN C_DRIVER;
LOOP
FETCH c_driver BULK COLLECT INTO GP_cursor_tab limit c_limit;
Exit when GP_cursor_tab.count = 0;
dbms_output.put_line('arr cur size: '||GP_cursor_tab.count);
FOR i IN GP_cursor_tab.FIRST .. GP_cursor_tab.LAST LOOP
vquery:= 'Select * from histwide';
Execute immediate vquery BULK COLLECT INTO GP_Insert_tab;
dbms_output.put_line('arr size: '||GP_Insert_tab.count);
-- FORALL j IN INDICES OF GP_Insert_tab
FORALL j in GP_Insert_tab.first..GP_Insert_tab.last --works too
Insert into skuhist --scpomgr.skuhist
values(GP_Insert_tab(j).f1
/*GP_Insert_tab(i).startdate
,1
,10080
,GP_Insert_tab(i).qty
,GP_Insert_tab(i).item
,GP_Insert_tab(i).loc*/
);
End Loop;
Exit when c_driver%NOTFOUND; --it should be at end of the loop.
End Loop;
Close C_DRIVER;
END;
/
Hope it will help you
How to retrieve list of employees for each department from table EMP into a comma-delimited new table
something like:
[column x:ie deptno] [column y:ie ename]
--------------------------
7 Jesus, María, José
5 Staz, Przemek, Tomek
6 John, Jane, Bob
below table is where I want to put my result from Function concatenate_list compilation
CREATE TABLE Z
(
x NUMBER(2) NOT NULL,
y VARCHAR2 (4000) NOT NULL
);
SET SERVEROUTPUT ON
CREATE OR REPLACE FUNCTION concatenate_list (xy_cursor IN SYS_REFCURSOR)
RETURN VARCHAR2
IS
lret VARCHAR2(30000);
ltemp VARCHAR2(30000);
BEGIN
LOOP
FETCH xy_cursor
INTO ltemp;
EXIT WHEN xy_cursor%notfound;
lret := lret || ',' || ltemp;
END LOOP;
RETURN LTRIM(lret, ',');
END;
/
SHOW ERRORS
how to insert the results from "Function concatenate_lit compile" and get a result as mentioned above.
Maybe using something like this:
INSERT INTO Z( x, y) SELECT e1.x,
concatenate_list(CURSOR(SELECT e2.y FROM EMP e2 WHERE e2.x= e1.x));
but how to set it up form inside the PL/SQL block
This may help you.
declare
type cur_name is ref cursor;
emp_name cur_name;
v_ename emp.ename%type;
v_all_ename varchar2(1000);
v_deptno emp.deptno%type;
cursor c is select deptno,cursor(select ename from emp e where e.deptno=f.deptno) from emp f group by deptno;
begin
open c;
loop
fetch c into v_deptno,emp_name;
exit when c%notfound;
loop
fetch emp_name into v_ename;
exit when emp_name%notfound;
v_all_ename:=v_all_ename||v_ename;
v_all_ename:=v_all_ename||',';
end loop;
dbms_output.put_line(v_deptno||' '||v_all_ename);
v_all_ename:='';
end loop;
close c;
end;
I have a log table and the table has a varchar2 field which holds xml string like below:
In this example ClientName attribute did not change but Clientsurname changed.
I want to capture changed columns and their previous and new values.
The log table contains millions of records.
Which method can you suggest for parsing this data in an efficient way?
<r>
<columntag nameattribute="ClientName">
<new_value>Jeffrey</new_value>
<previous_value>Jeffrey</previous_value>
</columntag>
<columntag nameattribute="ClientSurname">
<new_value>Dijk</new_value>
<previous_value>Disk</previous_value>
</columntag>
</r>
Thank you
not 100% sure the below is what you are after but it should give you some ideas about how to go about it. Hope it is helpfull
CREATE TABLE "RM4SERV"."LOG_TEST" ( "TESTLOG" VARCHAR2(4000 BYTE))
Insert into RM4SERV.LOG_TEST (TESTLOG) values ('<r><columntag nameattribute="ClientName"><new_value>Jeffery</new_value><previous_value>Jeffery</previous_value> </columntag><columntag nameattribute="ClientSurname"><new_value>Dijk</new_value><previous_value>Disk</previous_value></columntag></r>');
Insert into RM4SERV.LOG_TEST (TESTLOG) values ('<r><columntag nameattribute="ClientName"><new_value>Jeffery</new_value><previous_value>Jeffery</previous_value> </columntag><columntag nameattribute="ClientSurname"><new_value>Disk</new_value><previous_value>Disk</previous_value></columntag></r>');
Insert into RM4SERV.LOG_TEST (TESTLOG) values ('<r><columntag nameattribute="ClientName"><new_value>Jeffery</new_value><previous_value>Jim</previous_value> </columntag><columntag nameattribute="ClientSurname"><new_value>Dijks</new_value><previous_value>Diskett</previous_value></columntag></r>');
declare
v_logrec varchar2(4000) := null;
v_recnum number := 0;
cursor c_logs is
select testlog from log_test;
cursor c_records is
select extractValue(x.column_value, '/columntag/#nameattribute') as column_name,
extractValue(x.column_value, '/columntag/new_value') as new_value,
extractValue(x.column_value, '/columntag/previous_value') as previous_value
from TABLE(XMLSequence(extract(xmltype.createxml(v_logrec), '//columntag'))) x
where extractValue(x.column_value, '/columntag/new_value') != extractValue(x.column_value, '/columntag/previous_value');
begin
for v_log in c_logs loop
v_logrec := v_log.testlog;
v_recnum := v_recnum + 1;
dbms_output.put_line(v_recnum);
for v_rec in c_records loop
SYS.dbms_output.put_line(v_rec.column_name || ' : *' || v_rec.new_value || '* : *' || v_rec.previous_value || '*');
end loop;
end loop;
end;
This would give you the below output (so Surname different in first record, nothing different in the second and both different in the third)...
1
ClientSurname : Dijk : Disk
2
3
ClientName : Jeffery : Jim
ClientSurname : Dijks : Diskett