Looping in PL/SQL - sql

Basically I want to select data from a table based on id's of another table. So the algorithm goes:
foreach i in (select distinct id from table1)
{
select * from table2 where table2.id=i;
}
How to perform this function with an SQL query? I understand that we could use joins etc instead of looping, however, my requirement is such that I need to pass id's one by one in a for-loop only.

select * from table2 where table2.id IN (select distinct id from table1)

Use for instead of foreach.
Place PL/SQL into a begin .... end; block (but see the declare below)
The =i should read =i.id as i is the complete record from the select statement, but you're only interested in its id field.
In PL/SQL, a select statement must be fetched into a variable. Therefore, you have to declare an according variable: r table1%rowtype
The declaration of such variables is in the declare ... section of PL/SQL blocks.
The "algorithm" then becomes
declare
r table2%rowtype;
begin
for i in (select distinct id from table1) loop
select * into r from table2 where table2.id = i.id;
end loop;
end;

Related

Can I transfer SQL Query into a FUNCTION?

Currently, I have some SQL queries which looks like this:
Drop Table X;
Create Table X(id INTEGER);
Insert Into X
select ..
from..
where a.name = GIVENNAME;
Select SUM(..)
from ..
..
order by date desc;
And I want to put all these into a SQL Function, where I can choose the Parameter "GIVENNAME" when I call the function.
Is there a way to make this possible?
I would know how to do it in JSON/Java, but I have really no clue how to make it as a Function in SQL (using Oracle).
Edit:
After pointing out some things, I want to add my current code:
DROP TABLE TEMPTABLE;
CREATE TABLE TEMPTABLE
(mitID INTEGER);
INSERT INTO TEMPTABLE
select m.mitid
from mitarbeiter m
inner join abteilungen a on m.abt = a.abtid
where a.abtname = #GIVENNAME;
select SUM(g.kosten)
from gehaelter g
left outer join gehaelter k
on g.mitarbeiter = k.mitarbeiter
and g.vondatum < k.vondatum
where k.mitarbeiter is null AND g.mitarbeiter in (select * from TEMPTABLE)
order by g.vondatum desc;
I'm currently more interested in a working solution than a nice & clean one
Fortunately you can have both:
create or replace function get_sum_kosten
( p_givenname in abteilungen.abtname%type )
return number
as
return_value number;
begin
select SUM(g.kosten)
into return_value
from gehaelter g
left outer join gehaelter k
on g.mitarbeiter = k.mitarbeiter
and g.vondatum < k.vondatum
where k.mitarbeiter is null
AND g.mitarbeiter in (select m.mitid
from mitarbeiter m
inner join abteilungen a on m.abt = a.abtid
where a.abtname = P_GIVENNAME
)
;
return return_value;
end;
Possible? Yes. Recommended? No.
For any DDL, you'd have to use dynamic SQL (EXECUTE IMMEDIATE). If queries are complex, those commands will be difficult to maintain.
INSERT is a DML, but you can't use it in a function, unless it is an autonomous transaction (and you'll have to commit (or rollback) within the function).
If it were a procedure, you'd - at least - avoid the last problem I mentioned. If you're returning something, use an OUT parameter.
Can't you use a (global) temporary table, instead? Create it once, use it many times. I understand that your code might be very complex and maybe it really can't fit into a single SELECT statement, but you should - at least - try to do that job in an Oracle spirit (i.e. it is not MS SQL Server).
example of procedure
https://www.sitepoint.com/stored-procedures-mysql-php/
like this
DELIMITER $$
CREATE PROCEDURE `avg_sal`(out avg_sal decimal)
BEGIN
select avg(sal) into avg_sal from salary;
END

How to store multiple rows in a variable in pl/sql function?

I'm writing a pl/sql function. I need to select multiple rows from select statement:
SELECT pel.ceid
FROM pa_exception_list pel
WHERE trunc(pel.creation_date) >= trunc(SYSDATE-7)
if i use:
SELECT pel.ceid
INTO v_ceid
it only stores one value, but i need to store all values that this select returns. Given that this is a function i can't just use simple select because i get error, "INTO - is expected."
You can use a record type to do that. The below example should work for you
DECLARE
TYPE v_array_type IS VARRAY (10) OF NUMBER;
var v_array_type;
BEGIN
SELECT x
BULK COLLECT INTO
var
FROM (
SELECT 1 x
FROM dual
UNION
SELECT 2 x
FROM dual
UNION
SELECT 3 x
FROM dual
);
FOR I IN 1..3 LOOP
dbms_output.put_line(var(I));
END LOOP;
END;
So in your case, it would be something like
select pel.ceid
BULK COLLECT INTO <variable which you create>
from pa_exception_list
where trunc(pel.creation_Date) >= trunc(sysdate-7);
If you really need to store multiple rows, check BULK COLLECT INTO statement and examples. But maybe FOR cursor LOOP and row-by-row processing would be better decision.
You may store all in a rowtype parameter and show whichever column you want to show( assuming ceid is your primary key column, col1 & 2 are some other columns of your table ) :
SQL> set serveroutput on;
SQL> declare
l_exp pa_exception_list%rowtype;
begin
for c in ( select *
from pa_exception_list pel
where trunc(pel.creation_date) >= trunc(SYSDATE-7)
) -- to select multiple rows
loop
select *
into l_exp
from pa_exception_list
where ceid = c.ceid; -- to render only one row( ceid is primary key )
dbms_output.put_line(l_exp.ceid||' - '||l_exp.col1||' - '||l_exp.col2); -- to show the results
end loop;
end;
/
SET SERVEROUTPUT ON
BEGIN
FOR rec IN (
--an implicit cursor is created here
SELECT pel.ceid AS ceid
FROM pa_exception_list pel
WHERE trunc(pel.creation_date) >= trunc(SYSDATE-7)
)
LOOP
dbms_output.put_line(rec.ceid);
END LOOP;
END;
/
Notes from here:
In this case, the cursor FOR LOOP declares, opens, fetches from, and
closes an implicit cursor. However, the implicit cursor is internal;
therefore, you cannot reference it.
Note that Oracle Database automatically optimizes a cursor FOR LOOP to
work similarly to a BULK COLLECT query. Although your code looks as if
it fetched one row at a time, Oracle Database fetches multiple rows at
a time and allows you to process each row individually.

PLSQL Variables

I'm new to programming and I was wonder how I would declare a variable I can use through out my code.
What I want to achieve is :
Myvariable = (select Column from table1 where column =1234 group by column);
select column from table2 where column in (myvariable);
select column from table3 where column in (myvariable);
and etc
Thanks in advance :)
If you're using PL/SQL Developer to access an Oracle database you can create a Test window (File - New - Test Window) with code similar to the following:
DECLARE
myVariable TABLE1.COLUMN%TYPE := 1234;
BEGIN
FOR aRow2 IN (SELECT COLUMN
FROM TABLE2
WHERE COLUMN = myVariable)
LOOP
DBMS_OUPUT.PUT_LINE('Do something with ''aRow2''');
END LOOP;
COMMIT;
FOR aRow3 IN (SELECT COLUMN
FROM TABLE3
WHERE COLUMN = myVariable)
LOOP
DBMS_OUPUT.PUT_LINE('Do something with ''aRow3''');
END LOOP;
COMMIT;
END;
You'll need to edit the above to do whatever you want with the rows from TABLE2 and TABLE3.
Best of luck.
this is not really a variable... you can write the sql like this
select column
from table2
where column in (select Column from table1 where column =1234 group by column);
You can use a temporary TEMPORARY TABLE bound to the current session:
DECLARE GLOBAL TEMPORARY TABLE temp
{
column INTEGER
}
ON COMMIT DELETE ROWS -- Specify PRESERVE ROWS if you want to keep them through commits
NOT LOGGED
ON ROLLBACK DELETE ROWS -- Remove this line if you want to keep rows when you rollback a transaction
You load the table:
insert into temp1 (select Column from table1 where column =1234 group by column)
Then you can use the data using generic code:
select column from table2 where column in (select column from temp1);
select column from table3 where column in (select column from temp1);

Oracle dynamic table creation on runtime

I make to try a search engine.Scenario like that; I have a table that it contains text context and I'm fetching some records what suitable according to my query then I want to transfer this founded text of id on a table that created dynamicly on runtime. My sql code as follow but this error
"expression is of wrong type"
SQL
declare
v_table dbms_sql.number_table;
begin
select a_id bulk collect into v_table from (select a_id from b where length(b_data) > 4);
select * from a where a_id in v_table;
end;
DBMS_SQL.NUMBER_TABLE is an associative array:
Unlike a database table, an associative array:
Does not need disk space or network operations
Cannot be manipulated with DML statements
You can select into an associative array, but you cannot use it as a table in a select.
You could to the select into with a nested table but you still couldn't use that in a select if the type is declared within your block because it would be a PL/SQL type that isn't valid within an SQL statement.
You would need a nested table type defined in SQL (i.e. not in PL/SQL) to achieve this. There is a built-in type to make it easier, but you can define your own:
declare
v_table sys.odcinumberlist;
v_table2 sys.odcinumberlist;
begin
select a_id bulk collect into v_table
from (select a_id from b where length(b_data) > 4);
select a.a_id bulk collect into v_table2
from table(v_table) vt
join a on a.a_id = vt.column_value;
end;
/
anonymous block completed
The second select you showed is incomplete so I've made one up. Your first doesn't need the nested select, that can be simplified to:
select a_id bulk collect into v_table
from b
where length(b_data) > 4;
Hopefully you're dong something with the collection before your second select, otherwise it's a bit pointless; you could just join a to b in the second select to get the same result:
select a.<columns> ...
from a
join b on b.a_id = a.a_id
where length(b.b_date) > 4;

selecting multiple rows into single variable and to use it in where clause of another delete query

As we can select multiple rows into single variable declared as some_table.rowtype in SQL. In the same way I want to fetch multiple row values of single column into a variable. So,
How can I declare it's variable type?
Can I use that in where clause?
Can I iterate through that variable(collection) values?
I want to do it in a stored procedure to delete one or more table records matching that variable values.
Does this work for you?
DELETE FROM myTable
WHERE myTable.ID IN
(SELECT ID
FROM myOtherTable
WHERE myOtherCondition)
CREATE TYPE number_table AS TABLE OF NUMBER;
/
DECLARE
my_number_table number_table;
BEGIN
SELECT some_column BULK COLLECT INTO my_number_table FROM some_table;
DELETE FROM some_other_table WHERE some_column IN (
SELECT column_value FROM TABLE(CAST(my_number_table AS number_table))
);
END;
/