How to loop through keys values | Oracle | - sql

I am stuck with this approach not getting. How do I need to go with it.
I have table which as column -> KEYS. This as more then 500+ keys
Like below the data would be :
KEYS
12RTY
UIIJ9
GHSJ8
HJSKI
I should not be making use of cursor need to loop one by one values from column : KEYS to my PLSQL logic
This is how I want to convert that data to comma separated format and used in my loop to read one by one value
(key1,key2,key3....)
My code :
DECLARE
v_name varchar2(10 char);
BEGIN
FOR v_counter in 1..(key1,key2,key3..) LOOP
select NAME into v_name from EMP where KEY=v_counter;
DBMS_OUTPUT.PUT_LINE(v_name)
END;
END;
I would have made use of list_agg but only 10 values can be created as comma separated.
More then that it throws error like exceeded 4000

You can use a cursor:
DECLARE
v_name varchar2(10 char);
BEGIN
FOR v_row IN (SELECT key FROM your_key_table)
LOOP
select NAME into v_name from EMP where KEY=v_row.key;
DBMS_OUTPUT.PUT_LINE(v_name);
END LOOP;
END;
/
But you could combine the two queries into one:
DECLARE
v_name varchar2(10 char);
BEGIN
FOR v_row IN (SELECT e.name
FROM your_key_table ykt
INNER JOIN emp e
ON (ykt.key = e.key)
ORDER BY ykt.key)
LOOP
DBMS_OUTPUT.PUT_LINE(v_row.name);
END LOOP;
END;
/
db<>fiddle here

Instead of using a cursor, you can use BULK COLLECT to store all of the keys into a collection, then loop through that collection selecting and printing the data you are looking for.
DECLARE
TYPE keys_t IS TABLE OF your_schema.key_table.keys%TYPE;
l_keys keys_t;
l_name your_schema.emp.name%TYPE;
BEGIN
SELECT keys
BULK COLLECT INTO l_keys
FROM your_schema.key_table;
FOR i IN 1 .. l_keys.COUNT
LOOP
SELECT NAME
INTO l_name
FROM EMP
WHERE KEY = l_keys (i);
DBMS_OUTPUT.PUT_LINE (l_name);
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL; --No employee exists with that key
END LOOP;
END;
If you are just trying to tie the keys to EMPs, it would probably make more sense to just do a join.

Related

how to get multiple records by passing parameter to where clause in oracle pl/sql

table :
create table emp
(
E_ID number,
E_NAME varchar2(30)
);
select * from emp;
101 name1
102 name2
My code:
declare
v1 varchar2(30) := '101,102';
begin
for i in (select e_id,e_name
from emp
where e_id in (v1)) loop
dbms_output.put_line(i.e_id);
end loop;
end;
/
ISSUE:
Getting ORA -01722:invalid number
Please help to understand this issue and suggest me the solution.
It is syntax error.
E_ID is of number type and you are comparing it will v1 which is varchar2 type.
Welcome to SO. A great place to ask questions: I can see what you're trying to do. Syntactically, you'd be forgiven for trying to query your table using the "IN" clause, but as others have said, this can not be done where you have committed your numeric values into a varchar2. In anycase, an array or a collection, (even if you had created one) it isn't an easy option here. But you do have a variety of solutions open to you:
1/ Place your numbers into an Array and use a condition in your loop and check that e_id forms part of the your array. But in-elegant!
2/ Create a global temporary table and add your numbers in and add the table into your query, specify a join.
3/ Create some dynamic PL/SQL using a Ref Cursor. I've included an example for you below, using your table (emp) and values. In this case, you'd be able to build up your string according to the values you want to query. See below. The varchar2 string: sqlString can be manipulated however you want. Paste into a test-harness and see. Hope it helps
declare
type refCursor is ref cursor;
tableCursor refCursor;
emp_record emp%rowtype;
sqlString varchar2(200);
begin
-- Dynamic SQL statement with placeholder:
sqlString := 'SELECT * FROM emp WHERE e_id in
(101, 102)';
-- Open cursor:
open tableCursor for sqlString;
-- Fetch rows from result set one at a time:
loop
fetch tableCursor
into emp;
exit when tableCursor%notfound;
dbms_output.put_line(emp.e_id);
end loop;
-- Close cursor:
close tableCursor;
end;

Print multiple rows from select statement

I have this table :
| Pattern |
----------------------
|category |varchar|
|patternexpr |varchar|
For example in this table I can have a category ISBN and its pattern to recognize it.
I want to create a procedure which takes three arguments : a table T, one of its column C and a category. I want to print every rows in column C in T table which respect the pattern associated.
This is what I did (Updated with the correct answer):
CREATE OR REPLACE PROCEDURE Recognize(T varchar,C varchar,catego varchar)
IS
v_patt Pattern.CATEGOR%Type;
BEGIN
SELECT patternexpr INTO v_patt
FROM Pattern WHERE CATEGOR=catego;
FOR myrow IN (SELECT C FROM T WHERE REGEXP_LIKE(C, v_patt) LOOP
dbms_output.put_line(myrow.C);
END LOOP;
END;
/
How can I declare a cursor to print my result without knowing the value of my variable patt in the "DECLARE" place ? Should I add another declare and begin...end bloc after the first query ? What is the best way to do it ?
(I'm working on Oracle SGBD)
Use REF CURSOR to fetch records for this purpose.
CREATE OR REPLACE PROCEDURE Recognize(
T VARCHAR2,
C VARCHAR2,
catego VARCHAR2)
IS
v_patt Pattern.CATEGOR%Type;
v_cur_txt VARCHAR2(400);
TYPE cur_type
IS
REF
CURSOR;
v_cur cur_type;
v_c VARCHAR2(20);
BEGIN
SELECT patternexpr INTO v_patt FROM Pattern WHERE CATEGOR=catego;
v_cur_txt := 'SELECT '||C||' FROM '|| T ||' WHERE REGEXP_LIKE('||C||', '''||v_patt||''')';
OPEN v_cur FOR v_cur_txt;
LOOP
FETCH v_cur INTO v_c;
EXIT
WHEN v_cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_c);
END LOOP;
CLOSE v_cur;
END;
/
NOTE: : Include proper EXCEPTION handling in your code for NO_DATA_FOUND etc.Also as per Nicholas , make some validations by using dbms_assert package
In Oracle, you don't need an explicit cursor:
for myrow in (select c from t where regexp_like(c, patt) loop
dbms_output.put_line(myrow.c);
end loop;
I would call the pattern variable something like v_patt; that way, declared variables don't get confused with column names.

PL SQL Cursor For Loop: add up values in same variable and output the summed total

I'm trying to calculate the GPA within a function that accepts a studentID as an input. My issue is that the variable lv_gpa_calc isn't adding to itself when the loop works through the cursor set. I added the DBMS_OUTPUT.PUT_LINE to make sure that it's working through the cursor set correctly and that prints to the screen the correct individual row values of what lv_gpa_calc should be, but when it gets returned in the function in a SQL block, it isn't adding all of those values together. Can you not set a variable to itself within a CURSOR FOR LOOP?
Update: initializing the lv_gpa_calc fixed the problem where the variable value wasn't adding to itself.
CREATE OR REPLACE
FUNCTION CALCULATE_GPA
(p_studentID IN number)
RETURN NUMBER
IS
CURSOR cur_gpa IS
SELECT grade, grade_value, credit_hours
FROM grade
JOIN enrollment USING (Grade)
JOIN section USING (term_code, subject_code, course_number, section)
JOIN course USING (subject_code, course_number)
WHERE student_ID = p_studentID;
lv_gpa_calc NUMBER(4,2):=0;
BEGIN
FOR rec_gpa IN cur_gpa LOOP
lv_gpa_calc:= lv_gpa_calc + ((rec_gpa.grade_value * rec_gpa.credit_hours)/rec_gpa.credit_hours);
DBMS_OUTPUT.PUT_LINE(lv_gpa_calc);
END LOOP;
RETURN lv_gpa_calc;
END CALCULATE_GPA;
The problem in your code is that variable lv_gpa_calc is not initialized. Adding whatever to NULL will result as NULL.
Simplified working test case:
--DROP TABLE my_numbers;
CREATE TABLE my_numbers (
id NUMBER
);
/
BEGIN
FOR l_i IN 1..10 LOOP
INSERT INTO my_numbers VALUES (DBMS_RANDOM.RANDOM);
END LOOP;
COMMIT;
END;
/
SELECT * FROM my_numbers;
/
DECLARE
CURSOR cur IS
SELECT id
FROM my_numbers;
l_sum NUMBER(10) := 0;
BEGIN
FOR rec IN cur LOOP
l_sum := l_sum + rec.id;
DBMS_OUTPUT.PUT_LINE('l_sum = ' || l_sum);
END LOOP;
DBMS_OUTPUT.PUT_LINE('Sum = ' || l_sum);
END;
/
Important line is:
l_sum NUMBER(10) := 0;
Without initialization :=0 it won't work.

how to populate database using procedures

I have about 15 different Tables filled with different data and different entity relationships.
I need to create a script which will populate my database with the content of those tables.
After script is finished, i run it in cmd, using sqlplus and later START path to file
i have two different sql files, one named db_spec.sql and another db_body.sql.
In my db_body.sql i've created a procedure to store data from two tables which have 1:N relationship.
First table
CREATE TABLE LOCATION (
ID_LOCATION INTEGER NOT NULL,
LOCATION_NAME VARCHAR2 (20) NOT NULL,
POSTCODE INTEGER NOT NULL
);
ALTER TABLE LOCATION
ADD (CONSTRAINT PK_LOCATION PRIMARY KEY (ID_LOCATION));
Second table
CREATE TABLE ADDRESS (
ID_ADDRESS INTEGER NOT NULL,
STREET VARCHAR2 (20) NOT NULL,
HOUSE_NUMBER INTEGER NOT NULL,
FK_ID_LOCATION INTEGER NOT NULL
);
ALTER TABLE ADDRESS
ADD (CONSTRAINT PK_ADRESS PRIMARY KEY (ID_ADRESS));
ALTER TABLE ADRESS
ADD (CONSTRAINT FK_ADRESS_ID_LOCATION FOREIGN KEY
(FK_ID_LOCATION) REFERENCES LOCATION(ID_LOCATION));
Now i need to populate them using data. Let's say
LOCATION_NAME = "London"
POSTOCDE = "394505" ...and so on
I've created this script, but as i run it, nothing shows up, so there's obviously some error in it.
db_spec.sql script
CREATE OR REPLACE PACKAGE apartment AS
PROCEDURE fill_location(location_number NUMBER);
PROCEDURE fill_address(number_of_addresses NUMBER);
END apartment;
db_body.sql script
SET SERVEROUTPUT ON
SET LINESIZE 400
SET TIMING ON
CREATE OR REPLACE PACKAGE BODY address AS
PROCEDURE fill_location(location_number NUMBER) IS
p_location_name VARCHAR2(20);
p_postcode NUMBER (10,2);
BEGIN
FOR num IN 1..location_number LOOP
p_location_name := 'Location';
p_postcode := dbms_random.value(1000,9600);
p_postcode := p_postcode ||' '|| TO_CHAR(num);
INSERT INTO LOCATION (ID_LOCATION, LOCATION, POSTCODE)
VALUES (num, p_location_name, p_postcode);
dbms_output.put_line(num);
END LOOP;
END fill_location;
PROCEDURE fill_address(number_of_adresses NUMBER)IS
p_street_name VARCHAR(20);
p_house_number NUMBER (10,2);
p_id_address NUMBER(10);
CURSOR data IS
SELECT ID_LOCATION
FROM LOCATION;
BEGIN
FOR num_loop IN data LOOP
FOR num IN 1..number_of_adresses LOOP
p_street_name := 'Ulica';
p_house_number := dbms_random.value(1,99);
p_street_name := p_street_name ||' '|| TO_CHAR(num);
SELECT NVL(MAX(p_id_address)+1,1)
INTO p_id_address
FROM ADDRESS;
INSERT INTO ADDRESS (ID_ADDRESS, FK_ID_LOCATION, STREET, HOUSE_NUMBER)
VALUES (p_id_address, num_loop.ID_LOCATION, p_street_name, p_house_number);
dbms_output.put_line(num_loop.ID_LOCATION);
END LOOP;
END LOOP;
END fill_address;
END;
SHOW ERRORS;
Can you guys please help me fix the problem so the code will run and work fine? Any input is appreciated!
Btw oprema_stanovanja.polni_kraj = address.fill_location
Unless I'm missing it... COMMIT;
Based on the screenshot you issued, add a / to the end of your body and procedure definitions.
Firstly, the package posted doesn't compile without errors.
You would need to fix the following first:
Terminate FOR LOOP statements with END LOOP
Terminate BEGIN of procedures with END <procedure_name_here>
PL/SQL variable names need to be different than the actual column name to avoid ambiguity in the INSERT statement. [ You can't have LOCATION the column name and PL/SQL variable with the same name. Oracle cannot resolve which is to be used where in the INSERT]
To get you started, I have fixed the fill_location procedure below. Proceed similarly for the other procedure.
create or replace package body apartment as
procedure fill_location(location_number number) is
p_location_name varchar2(20);
p_postcode number(10,2);
begin
for num in 1.. location_number loop
p_location_name := 'location';
p_postcode := dbms_random.value(1000,9600);
p_location_name := p_location_name ||' '|| to_char(num);
insert into location (id_location, location_name, postcode)
values (num, p_location_name, p_postcode);
dbms_output.put_line(num);
end loop;
end fill_location;
Fix the above errors/suggestions until you get 'PL/SQL successfully compiled`. If you get 'compiled with errors' use "show errors" to find and fix any other errors.

Move large data between tables in oracle with bulk insert

I want to move 1 million rows of data to another table. Im using query:
insert into Table1
select * from Table2;
in my PL/SQL function. But this way is too slow.
How can I do this with Bulk Insert method?
Source and destination table has same structure.
Tables have hash partition and 1 index.
Forget about bulk insert. Because the insert into select is the best bulk you can load.
The fastest would be to disable the indexes (mark them unusable) and do this in a SINGLE
insert:
insert /*+ append */ into TARGET
select COLS
from SOURCE;
commit;
and rebuild the indexes using UNRECOVERABLE (and maybe even parallel).
PS: If the table is partitioned (Both source and target, you can even use parallel inserts)
FOLLOW UP:
Check the performance of the below select
SELECT /*+ PARALLEL(A 4)
USE_HASH(A) ORDERED */
YOUR_COLS
FROM
YOUR_TABLE A
WHERE
ALL_CONDITIONS;
If faster then
INSERT /*+ APPEND */
INTO
TARGET
SELECT /*+ PARALLEL(A 4)
USE_HASH(A) ORDERED */
YOUR_COLS
FROM
YOUR_TABLE A
WHERE
ALL_CONDITIONS;
USING Bulk Collect
Converting to collections and bulk processing can increase the volume and complexity of your code. If you need a serious boost in performance, however, that increase is well-justified.
Collections, an evolution of PL/SQL tables that allows us to manipulate many variables at once, as a unit. Collections, coupled with two new features introduced with Oracle 8i, BULK_COLLECT and FORALL, can dramatically increase the performance of data manipulation code within PL/SQL.
CREATE OR REPLACE PROCEDURE test_proc (p_array_size IN PLS_INTEGER DEFAULT 100)
IS
TYPE ARRAY IS TABLE OF all_objects%ROWTYPE;
l_data ARRAY;
CURSOR c IS SELECT * FROM all_objects;
BEGIN
OPEN c;
LOOP
FETCH c BULK COLLECT INTO l_data LIMIT p_array_size;
FORALL i IN 1..l_data.COUNT
INSERT INTO t1 VALUES l_data(i);
EXIT WHEN c%NOTFOUND;
END LOOP;
CLOSE c;
END test_proc;
this procedure created by rohit sahani......
using bulk collect i create this procedure
in this procedure i also did update and insert in table and
in this procedure i did collect data in bulk and then update in salary with the use of forall statement .
CREATE OR REPLACE PROCEDURE sam_proc_1(l_salary NUMBER)
IS
l_address VARCHAR(100) := 'karnatka';
a_address VARCHAR(100) := 'bihar';
c_limit PLS_INTEGER := 100;
TYPE employee_ids_t IS TABLE OF emp.emp_id%TYPE;
l_employee_ids employee_ids_t;
CURSOR employees_cur IS SELECT emp_id FROM emp;
-- CURSOR CUR1 IS SELECT EMP_ID, FNAME, ADDRESS FROM EMP;
BEGIN
OPEN employees_cur;
LOOP
FETCH employees_cur BULK COLLECT INTO l_employee_ids LIMIT c_limit;
EXIT WHEN l_employee_ids.COUNT = 0;
-----here start updating
FORALL indx IN 1 .. l_employee_ids.COUNT SAVE EXCEPTIONS
UPDATE emp a
SET a.salary = a.salary + l_salary
WHERE a.emp_id = l_employee_ids(indx);
commit;
END LOOP;
-----------
BEGIN
UPDATE emp v
SET v.address = l_address
WHERE v.address = a_address;
COMMIT;
EXCEPTION WHEN
OTHERS THEN
RAISE_APPLICATION_ERROR(-20001,'An error encountered while updating in address - '
||SQLCODE||' -ERROR- '||SQLERRM);
END;
---------
BEGIN
FOR I IN ( SELECT EMP_ID, FNAME, ADDRESS FROM EMP)
LOOP
INSERT INTO students
( stu_id, stu_name, stu_address)
VALUES
(i.emp_id, i.fname, i.address);
END LOOP;
COMMIT;
EXCEPTION WHEN
OTHERS THEN
RAISE_APPLICATION_ERROR(-20001,'An error encountered while inserting - '
||SQLCODE||' -ERROR- '||SQLERRM);
END;
------------
EXCEPTION
WHEN OTHERS
THEN
IF SQLCODE = -24381
THEN
FOR indx IN 1 .. SQL%BULK_EXCEPTIONS.COUNT
LOOP
-- Caputring errors occured during update
DBMS_OUTPUT.put_line( SQL%BULK_EXCEPTIONS (indx).ERROR_INDEX
||','||
SQL%BULK_EXCEPTIONS (indx).ERROR_CODE);
--<You can inset the error records to a table here>
END LOOP;
ELSE
RAISE;
END IF;
END sam_proc_1;
-----END PROCEDURE;c