say i have the table XX_TEST_DATA and its being used to populate a nested table inside a PL/SQL block below (please note the comments):
DECLARE
CURSOR XX_DATA_CUR
IS
SELECT *
FROM XX_TEST_DATA;
TYPE TYP_XX_TEST IS TABLE OF XX_TEST_DATA%ROWTYPE INDEX BY PLS_INTEGER;
XX_REC TYP_XX_TEST;
BEGIN
OPEN XX_DATA_CUR;
FETCH XX_DATA_CUR
BULK COLLECT
INTO XX_REC;
CLOSE XX_DATA_CUR;
for i in 1..XX_REC.count loop
XX_REC(i).BATCH_NAME := 'Batch 1'; -- This is the Line
end loop;
END;
I would like to assign the value "Batch 1" to ALL records inside the nested table. Would this be possible without looping through all the records?
Something like below:
BEGIN
OPEN XX_DATA_CUR;
FETCH XX_DATA_CUR
BULK COLLECT
INTO XX_REC;
CLOSE XX_DATA_CUR;
XX_REC.BATCH_NAME := 'Batch 1'; --
END;
I know the above block would not work, but I was hoping to achive something like that.
DDL of the Test Table
Create table XX_TEST_DATA
(
XX_ID NUMBER
, XX_DATA1 VARCHAR2(100)
, XX_DATA2 VARCHAR2(100)
, BATCH_NAME VARCHAR2(100)
);
The value could be set as part of the SELECT and then there would be no need to update the collection.
DECLARE
TYPE TYP_XX_TEST IS TABLE OF XX_TEST_DATA%ROWTYPE INDEX BY PLS_INTEGER;
XX_REC TYP_XX_TEST;
BEGIN
SELECT XX_ID, XX_DATA1, XX_DATA2, 'Batch 1'
BULK COLLECT INTO XX_REC
FROM XX_TEST_DATA;
END;
/
Related
I am using this code to see if it will work for a procedure. I want to be able to make a procedure in which i can decide what data to extract by typing the time ('jan-2020') in which it is recorded and also to decide in which table i want to place the data in (january_2020). i get the error that the table is not able to be used in this context. What do i have to change in the code to be in the right context?
Is it because i am using dynamic sql in a loop that requires the loop to be executed to put the data in the table? or is it because i am using %rowtype as the attribute for the table ALL_DATA to create its own columns? If it is any of these what should i do to change it?
DECLARE
time_v varchar2(9);
table_v varchar2(200);
sql_code varchar2(300);
TYPE Copied_Table IS TABLE OF Gastos%ROWTYPE;
All_Data Copied_Table;
BEGIN
time_v := 'jan-2020';
SELECT *
BULK COLLECT INTO All_Data FROM Gastos
Where TO_CHAR(DATE_, 'MON-YYYY') = UPPER(time_v);
FOR I in All_Data.First .. All_Data.Last LOOP
sql_code := 'INSERT INTO :table_v ( DATE_, DESCRIPTION, ORIGINAL_DESCRIPTION, AMOUNT,
TRANSACTION_TYPE, CATEGORY, ACCOUNT_NAME)
Values ( ALL_Data(i).date_, ALL_Data(i).description, ALL_Data(i).original_description,
ALL_Data(i).amount, ALL_Data(i).transaction_type, ALL_Data(i).category, ALL_Data(i).account_name)';
table_v := january_2020;
execute immediate sql_code
using table_v;
END LOOP;
END upload_monthly_expenses;
Pass table name as input parameter and replace bind variable with normal variable for the table name and concatenate it to the DML statement.Modify your code as below,
CREATE OR REPLACE PROCEDURE upload_monthly_expenses(table_v IN VARCHAR2,time_v IN VARCHAR2) AS
DECLARE
sql_code varchar2(300);
TYPE Copied_Table IS TABLE OF Gastos%ROWTYPE;
All_Data Copied_Table;
BEGIN
SELECT *
BULK COLLECT INTO All_Data FROM Gastos
Where TO_CHAR(DATE_, 'MON-YYYY') = UPPER(time_v);
FOR I in All_Data.First .. All_Data.Last LOOP
sql_code := 'INSERT INTO '||table_v||' ( DATE_, DESCRIPTION, ORIGINAL_DESCRIPTION, AMOUNT,
TRANSACTION_TYPE, CATEGORY, ACCOUNT_NAME)
Values ( ALL_Data(i).date_, ALL_Data(i).description, ALL_Data(i).original_description,
ALL_Data(i).amount, ALL_Data(i).transaction_type, ALL_Data(i).category, ALL_Data(i).account_name)';
execute immediate sql_code;
END LOOP;
END;
From a PL/SQL block procedure can be executed as below,
BEGIN
upload_monthly_expenses('jan-2020','january_2020');
END;
I have a procedure in which I want to fetch all records from cte into Names variable. But this code is not writing into names from CTE. How can I fetch records into names so that I can later loop through names and get content of field_name?
CREATE OR REPLACE PROCEDURE sp_market
IS
Names VARCHAR2(32767);
BEGIN
WITH CTE(sqql) As
(
SELECT field_name sqql FROM pld_medicare_config
)
SELECT sqql into Names from CTE;
END sp_market;
SELECT sqql into Names from CTE;
You are assigning multiple rows returned from table to a variable, which will fail.
You could simply use a CURSOR FOR LOOP which will create an implicit cursor and you can loop through the names:
CREATE OR REPLACE PROCEDURE sp_market IS
BEGIN
FOR i IN (
SELECT field_name
FROM pld_medicare_config
)
LOOP
-- Your logic goes here
dbms_output.put_line(i.field_name);
END LOOP;
END;
/
I think your best bet is to create a associative array and use BULK COLLECT to populate the table. In its simplest form, the code would look like this:
CREATE OR REPLACE PROCEDURE sp_market IS
TYPE lt_names IS TABLE OF VARCHAR2(32767) INDEX BY PLS_INTEGER;
l_tNames lt_names;
BEGIN
SELECT field_name
BULK COLLECT INTO l_tNames
FROM pld_medicare_config
IF l_tNames.COUNT() > 0 THEN
FOR i IN l_tNames.FIRST..l_tNames.LAST LOOP
NULL; --REPLACE WITH YOUR LOGIC
END LOOP;
END IF;
END;
/
A few notes:
I'm assuming that you've set MAX_STRING_SIZE to EXTENDED. Otherwise, you'll have an issue with VARCHAR2 that big.
As I said, that is the simplest way to do this. If you're expecting a huge result set, you'll want to look into chunking it up. This Oracle blog post is very helpful in giving you multiple options for how to perform bulk processing. (Blog Post)
I'm writing a function that needs to manipulate multiple rows at the same time and they need to be indexed. After several hours of reading about Oracle pl/sql I figured I could create a nested table kind of collection. Since I couldn't find a definitive answer and trial/error method takes way to long.
Here is question part:
QUESTION: What is the best practice to populate a nested table collection? Oracle PL/SQL
type partsTable is table of Parts_north_wing%rowtype;
pt PartsTable;
index number;
cursor pCursor is select * from Parts_north_wing;
begin
index := 1;
open pCursor;
loop
fetch pCursor into tempRow;
pt(index) := tempRow;
index := index + 1;
exit when pCursor%notfound;
end loop;
close pCursor;
A cursor FOR LOOP is almost always the best way to process rows in PL/SQL. It's simpler than the OPEN/FETCH/CLOSE method - no need to declare variables and manipulate cursors. It's also faster since it automatically bulk collects the results.
begin
for pt in
(
select parts_north_wing.*, rownum row_index
from parts_north_wing
) loop
--Do something here
null;
end loop;
end;
/
Try this. Hope this helps you to clear some of your concepts.
--Create a dummy object tyep
CREATE OR REPLACE TYPE av_obj
IS
OBJECT
(
ADD1 VARCHAR2(100),
ADD2 VARCHAR2(100) );
--Create a nested tale type
CREATE OR REPLACE TYPE AV_TT
IS
TABLE OF AV_OBJ;
--Bulk collect into nested table type
DECLARE
av_nested_tab AVROY.AV_TT;
BEGIN
SELECT avroy.av_obj(LEVEL
||'add1',LEVEL
||'add2') BULK COLLECT
INTO av_nested_tab
FROM DUAL
CONNECT BY LEVEL < 10;
END;
I'm having a heck of a time trying to find an example of this being done. I have a procedure, and as part of that procedure I want to store the results of a SELECT statement so that I can work against that set, and then use it as a reference to update the original records when it's all done.
The difficulty I'm having is in declaring the temporary table variable. Here's an example of what I'm trying to do:
PROCEDURE my_procedure
IS
output_text clob;
temp_table IS TABLE OF MY_TABLE%ROWTYPE; -- Error on this line
BEGIN
SELECT * BULK COLLECT INTO temp_table FROM MY_TABLE WHERE SOME_DATE IS NULL;
-- Correlate results into the clob for sending to email (working)
-- Set the SOME_DATE value of the original record set where record is in temp_table
I get an error on the second occurrence of IS, saying that it is an unexpected symbol. This suggests to me that my table variable declaration is either wrong, or in the wrong place. I've tried putting it into a DECLARE block after BEGIN, but I just get another error.
Where should this declaration go? Alternatively, if there is a better solution I'll take that too!
CREATE OR REPLACE PROCEDURE PROCEDURE1 AS
output_text clob;
type temp_table_type IS TABLE OF MY_TABLE%ROWTYPE;
temp_table temp_table_type;
BEGIN
SELECT * BULK COLLECT INTO temp_table FROM MY_TABLE;
END PROCEDURE1;
or
CREATE OR REPLACE PROCEDURE PROCEDURE1 ( output_text OUT clob ) IS
type temp_table_type IS TABLE OF MY_TABLE%ROWTYPE
INDEX BY BINARY_INTEGER;
temp_table temp_table_type;
BEGIN
SELECT * BULK COLLECT INTO temp_table FROM MY_TABLE;
FOR indx IN 1 .. temp_table.COUNT
LOOP
something := temp_table(indx).col_name;
END LOOP;
END PROCEDURE1;
I had a similiar problem and found this:
Selecting Values from Oracle Table Variable / Array?
The global temporary table can be used like a regular table, but its content is only temporary (deleted at end of session/transaction) and each session has its own table content.
If you don't need dynamic SQL this can be used as good solution:
CREATE GLOBAL TEMPORARY TABLE temp_table
(
column1 NUMBER,
column2 NUMBER
)
ON COMMIT DELETE ROWS;
PROCEDURE my_procedure
IS
output_text clob;
BEGIN
-- Clear temporary table for this session (to be sure)
DELETE FROM temp_table;
-- Insert data into temporary table (only for this session)
INSERT INTO temp_table SELECT * FROM MY_TABLE WHERE SOME_DATE IS NULL;
-- ...
END;
The only disadvantages are, in my opinion, that you got another table and that the temporary table is not dynamic.
I declared table type and set a value in it with using loop. I am having an error while I was casting this t_table
DECLARE
TYPE t_row IS RECORD
(
id NUMBER,
description VARCHAR2(50)
);
TYPE t_table IS TABLE OF t_row;
l_tab t_table := t_table();
BEGIN
FOR i IN 1 .. 10 LOOP
l_tab.extend();
l_tab(l_tab.last).id := i;
l_tab(l_tab.last).description := 'Description for ' || i;
END LOOP;
SELECT * from TABLE(CAST(l_tab AS t_table));
END
Best regards
Why do you want to do a select onto the the type? You would use the the TABLE() and the CAST rather if you have a collection in a column stored in a table.
You could just loop through the table in your code. Example:
for i in l_tab.first .. l_tab.last
loop
dbms_output.put_line(l_tab(i).id||' '||l_tab(i).description);
end loop;
Since l_tab is of type t_table, there's no need for the cast. But that's not your problem.
Your problem is that you're trying to reference a PL/SQL type in SQL, which you simply can't do. You can either remove the select as #hol suggested or make the type a database object (which will allow SQL to access it):
CREATE OR REPLACE TYPE t_row AS OBJECT
(
id NUMBER,
description VARCHAR2 (50)
);
CREATE OR REPLACE TYPE t_table AS TABLE OF t_row;
DECLARE
l_tab t_table := t_table ();
BEGIN
FOR i IN 1 .. 10 LOOP
l_tab.EXTEND ();
l_tab (l_tab.LAST) := t_row (i, 'Description for ' || i);
END LOOP;
FOR r IN (SELECT * FROM TABLE (l_tab)) LOOP
DBMS_OUTPUT.put_line (r.id);
END LOOP;
END;
There is a second problem with the initial code, in that you are running a select without telling the code what to do with it. Unlike some other procedural SQL extensions, PL/SQL does not allow you to implicitly return a handle to a resultset (prior to 12c). You must either handle it directly or explicitly return a ref_cursor that points to it. The code above has been update to primitively handle the result of the query.