PL/SQL FIRST LAST EXISTS - (cannot show table contents) - oracle

First, I have read the PL/SQL documentation repeatedly, no help. Second, I have googled for hours (usually being led here) and still cannot figure this out.
I have created an index table, which seems to have been successful - at least it did not throw any errors...
DECLARE
CURSOR cur_emps IS
SELECT employee_id, last_name, job_id, salary FROM employees ORDER BY employee_id;
TYPE t_emp_rec IS TABLE OF cur_emps%ROWTYPE
INDEX BY BINARY_INTEGER;
v_emp_rec_tab t_emp_rec;
BEGIN
FOR emp_rec IN cur_emps LOOP
v_emp_rec_tab(emp_rec.employee_id) := emp_rec;
END LOOP;
However, when I try to show what is in my index table. I fail...
Documentation says do something like this...
DECLARE
CURSOR cur_emps IS
SELECT employee_id, last_name, job_id, salary FROM employees ORDER BY employee_id;
TYPE t_emp_rec IS TABLE OF cur_emps%ROWTYPE
INDEX BY BINARY_INTEGER;
v_emp_rec_tab t_emp_rec;
BEGIN
FOR emp_rec IN cur_emps LOOP
v_emp_rec_tab(emp_rec.employee_id) := emp_rec;
END LOOP;
FOR i IN v_emp_rec_tab.FIRST..v_emp_rec_tab.LAST LOOP
IF v_emp_rec_tab.EXISTS(i)
THEN DBMS_OUTPUT.PUT_LINE(v_emp_rec_tab(i));
END IF;
END LOOP;
END;
... which gives me this...
ORA-06550: line 13, column 12:
PLS-00306: wrong number or types of arguments in call to 'PUT_LINE'
ORA-06550: line 13, column 12:
PL/SQL: Statement ignored
In place of
THEN DBMS_OUTPUT.PUT_LINE(v_emp_rec_tab(i));
. I have tried
emp_rec.last_name, v_emp_rec.last_name, cur_emps.last name
... it has been days now trying, can anyone help?
Thanks.
Daniel

You should iterate over an indexed table like this:
l_idx := v_emp_rec_tab.first;
while (l_idx is not null) loop
dbms_output.put_line( v_emp_rec_tab(l_idx).last_name );
l_idx := v_emp_rec_tab.next(l_idx);
end loop;
Otherwise you will get an exception when your table indexes are not consecutive.
See here

v_emp_rec_tab is of rowtype, and it's not possible to call as
DBMS_OUTPUT.PUT_LINE(v_emp_rec_tab(i));
but you can call as :
DBMS_OUTPUT.PUT_LINE(v_emp_rec_tab(i).last_name);
with only one column or you might concatenate like :
DBMS_OUTPUT.PUT_LINE(v_emp_rec_tab(i).last_name||' '||v_emp_rec_tab(i).employee_id);
for multiple columns.

Related

Insert into not working on plsql in oracle

declare
vquery long;
cursor c1 is
select * from temp_name;
begin
for i in c1
loop
vquery :='INSERT INTO ot.temp_new(id)
select '''||i.id||''' from ot.customers';
dbms_output.put_line(i.id);
end loop;
end;
/
Output of select * from temp_name is :
ID
--------------------------------------------------------------------------------
customer_id
1 row selected.
I have customers table which has customer_id column.I want to insert all the customer_id into temp_new table but it is not being inserted. The PLSQL block executes successfully but the temp_new table is empty.
The output of dbms_output.put_line(i.id); is
customer_id
What is wrong there?
The main problem is that you generate a dynamic statement that you never execute; at some point you need to do:
execute immediate vquery;
But there are other problems. If you output the generated vquery string you'll see it contains:
INSERT INTO ot.temp_new(id)
select 'customer_id' from ot.customers
which means that for every row in customers you'll get one row in temp_new with ID set to the same fixed literal 'customer_id'. It's unlikely that's what you want; if customer_id is a column name from customers then it shouldn't be in single quotes.
As #mathguy suggested, long is not a sensible data type to use; you could use a CLOB but only really need a varchar2 here. So something more like this, where I've also switched to use an implicit cursor:
declare
l_stmt varchar2(4000);
begin
for i in (select id from temp_name)
loop
l_stmt := 'INSERT INTO temp_new(id) select '||i.id||' from customers';
dbms_output.put_line(i.id);
dbms_output.put_line(l_stmt);
execute immediate l_stmt;
end loop;
end;
/
db<>fiddle
The loop doesn't really make sense though; if your temp_name table had multiple rows with different column names, you'd try to insert the corresponding values from those columns in the customers table into multiple rows in temp_new, all in the same id column, as shown in this db<>fiddle.
I guess this is the starting point for something more complicated, but still seems a little odd.

I am getting an error message in Oracle SQL stored procedure

I am trying the following code
CREATE OR REPLACE PROCEDURE sal2
AS
BEGIN
SELECT sal
FROM emp
END
And I'm getting this error
Error at line 7: PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following:
( begin case declare end exception exit for goto if loop mod
null pragma raise return select update while with
<< continue close current delete fetch lock
insert open rollback savepoint set sql execute commit forall
merge pipe purge
5. from emp
6. return 1
7. END
What I want to do is to get a sal column from emp table.
There are multiple issues with code as following;
create or replace procedure sal2
AS
Lv_var number; -- added variable for holding select value
BEGIN
select sal
Into lv_var -- into is needed in select
from emp; -- you missed this semicolon
-- you must use where clause as into variable can hold single value
-- so you must restrict this query to return only 1 record.
END; -- you missed this semicolon
/
Cheers!!
First you have to put the semicolon at the end of each line and after the END keyword. Then the SELECT statement is also wrong you have to load the result in a variable.
Here is the solution for multiple rows.
CREATE OR REPLACE PROCEDURE sal2
AS
CURSOR c_salaries IS SELECT salary FROM employees;
TYPE t_salaries IS TABLE OF c_salaries%rowtype INDEX BY BINARY_INTEGER;
v_salaries t_salaries;
BEGIN
OPEN c_salaries;
FETCH c_salaries BULK COLLECT INTO v_salaries;
CLOSE c_salaries;
END;
And one for a single row result.
CREATE OR REPLACE PROCEDURE sal2
AS
v_salary employees.salary%rowtype;
BEGIN
SELECT salary INTO v_salary
FROM employees WHERE employee_id = 100;
END;

How do I display a nested table in Oracle - PL/SQL?

I have an assignment where I'm supposed to create a user-defined function that will return all employees after 1968. My code is as follows:
First I create a new object, called emp_dobs, to hold the employees' firstname, lastname, and date of birth, using the same data types as the original employee table:
CREATE OR REPLACE TYPE emp_dobs AS OBJECT (
emp_fname VARCHAR2(20),
emp_lname VARCHAR2(20),
emp_dob DATE
);
/
Then I create emp_dobs_nested as a table of emp_dobs:
CREATE OR REPLACE TYPE emp_dobs_nested AS TABLE OF emp_dobs;
/
Lastly, I create a function that's supposed to return an emp_dobs_nested table:
CREATE OR REPLACE FUNCTION get_emp_dobs RETURN emp_dobs_nested
AS
dobs emp_dobs_nested;
BEGIN
SELECT emp_dobs(firstname, lastname, birthdate) BULK COLLECT INTO dobs
FROM employee
WHERE birthdate < TO_DATE('01-JAN-1968', 'DD-MON-YYYY');
RETURN dobs;
END;
/
There is a weird quirk with compiling emp_dob_nested, however, where Oracle SQL Developer will display a new tab labeled "Output Variables - Log," and only show EMP_FNAME and EMP_LNAME. Despite that, everything compiles.
Now I want to test the function and display its results to prove that it works, but when I try this:
DECLARE
dobs emp_dobs_nested;
BEGIN
dobs := get_emp_dobs;
DBMS_OUTPUT.PUT_LINE(dobs);
END;
/
I get this error:
Error report -
ORA-06550: line 5, column 5:
PLS-00306: wrong number or types of arguments in call to 'PUT_LINE'
ORA-06550: line 5, column 5:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
What am I missing here? Thanks.
You cannot pass the whole collection to DBMS_OUTPUT, rather you must loop through it and display individual columns at each index.
DECLARE
dobs emp_dobs_nested;
BEGIN
dobs := get_emp_dobs;
FOR i IN 1..dobs.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE(dobs(i).emp_fname||','||dobs(i).emp_lname||','||dobs(i).emp_dob);
END LOOP;
END;
/
You can also use TABLE function to unnest a collection of objects into a relational resultset:
select * from table( get_emp_dobs )
Live demo: http://sqlfiddle.com/#!4/8cfb2/1
Another way of display is to use XML, try
DBMS_OUTPUT.PUT_LINE(XMLTYPE(dobs));
why not a create a view instead?
CREATE VIEW emp_view
AS
SELECT firstname, lastname, birthdate
FROM employee
WHERE birthdate < TO_DATE('01-JAN-1968', 'DD-MON-YYYY');
You can also try this,
DECLARE
dobs emp_dobs_nested;
i NUMBER := 0;
BEGIN
dobs := get_emp_dobs;
LOOP
i := dobs.NEXT(i);
DBMS_OUTPUT.PUT_LINE(dobs(i).emp_fname||','||dobs(i).emp_lname||','||dobs(i).emp_dob);
IF i = dobs.LAST THEN
EXIT;
END IF;
END LOOP;
END;
/

Error trying to print Collection variable by index

Can anyone identify what is wrong with the following code. specifically the first dbms_output line. The second one prints fine. But first one gives this error:
Error at line 2
ORA-06550: line 15, column 53:
PLS-00201: identifier 'MYCOLL' must be declared
ORA-06550: line 15, column 1:
PL/SQL: Statement ignored
DECLARE
CURSOR c1
IS
SELECT sub_provider_address_id sub_id, phone, extension
FROM sub_provider_address;
TYPE coll_type IS TABLE OF c1%ROWTYPE;
my_coll coll_type;
BEGIN
OPEN c1;
FETCH c1
BULK COLLECT INTO my_coll;
dbms_output.put_line(' my_coll first row id has '|| mycoll(1).phone );
dbms_output.put_line(' my_coll now has '|| my_coll.last );
END;
You're declaring the variable as my_coll:
my_coll coll_type;
And on the line that's erroring you're referring to it as mycoll:
dbms_output.put_line(' my_coll first row id has '|| mycoll(1).phone );
So you're just missing an underscore, and it should be:
dbms_output.put_line(' my_coll first row id has '|| my_coll(1).phone );
Your coll_type is not an associated array type.
you have to use something like this:
TYPE coll_type IS TABLE OF c1%ROWTYPE index by pls_integer;
Use this as an example:
declare
cursor c1 is
select 1 id from dual
;
type coll_type is table of c1%ROWTYPE index by pls_integer;
l_coll_type coll_type;
begin
open c1;
fetch c1 bulk collect into l_coll_type;
close c1;
dbms_output.put_line('Output:' || l_coll_type(1).id);
end;
Output:1

Iterate over a column in PL/SQL

I have a table Emp with EmpID, Empname, Salary and I am trying to do a calculation for each employee. But I am having problems trying to iterate over each emp to do the calculation. I cant use explicit cursors though.
So right now I am just trying to create the list of empIDs:
Declare
aRows Number;
eid emp_ID%TYPE;
Begin
Select Count(*)
Into aRows
from emp;
Select emp_ID
Into eid
From emp;
FOR days IN 1..Tot_Rows
Loop
Dbms_Output.Put_Line(eid);
eid := eid + 1;
End Loop;
END;
But I get the error:
PLS-00320: the declaration of the type of this expression is incomplete or malformed
The simplest way to iterate over the rows in a table in PL/SQL is to do something like
BEGIN
FOR employees IN (SELECT emp_id FROM emp)
LOOP
dbms_output.put_line( employees.emp_id );
END LOOP;
END;
Alternately, you could fetch all the EID values into a PL/SQL collection and iterate over the collection, as in this example
DECLARE
TYPE emp_id_tbl IS TABLE OF emp.emp_id%type;
l_emp_ids emp_id_tbl ;
BEGIN
SELECT emp_id
BULK COLLECT INTO l_emp_ids
FROM emp;
FOR i IN l_emp_ids .FIRST .. l_empnos.LAST
LOOP
dbms_output.put_line( l_emp_ids (i) );
END LOOP;
END;
If your query can return thousands of rows, however, fetching all the data into the collection may use more of the PGA memory than you'd like and you may need to fetch rows in chunks using the LIMIT clause. But that would seem to be getting ahead of ourselves at this point.
Justin Cave has explained how to do it, but to specifically look at the error you got, that was because of this:
eid emp_ID%TYPE;
When using the %TYPE you have to specify the table name as well as the column name:
eid emp.emp_ID%TYPE;
If you were selecting all the columns in the row you could also look at %ROWTYPE.
Your approach was also making two assumptions: that the initial select into eid found the lowest ID, which is by no means guaranteed; and that all the subsequent ID values are sequential. And you're declaring and populating aRows but referring to Tot_Rows.

Resources