Associative array related problem
Hi All,
When I am trying to use assotiative array in a select statement I recieve the following error:
ORA-06550: line 9, column 22:
PLS-00201: identifier 'COL1' must be declared
ORA-06550: line 9, column 22:
PLS-00201: identifier 'COL1' must be declared
ORA-06550: line 9, column 10:
PL/SQL: ORA-00904: : invalid identifier
ORA-06550: line 9, column 3:
PL/SQL: SQL Statement ignored
Here is the example
--create table MyTable (col1 varchar2(255), col2 varchar2(255))
declare
type m_ttMyTable
is table of MyTable%rowtype index by MyTable.col1%type;
m_tMyTable m_ttMyTable;
m_sCol2 varchar2(255);
begin
select m_tMyTable (col1).col2 /* works with ocntant: select m_tMyTable */('col1').col2
into m_sCol2
from MyTable
where rownum = 1;
end;
--drop table MyTableAny ideas how to workaround this?
Thanks
The only collection types SQL can query are ones defined in SQL using CREATE TYPE. That excludes associative arrays, as they are PL/SQL-only constructs. I'd recommend a nested table collection.
Some more suggestions:
www.williamrobertson.net/documents/collection-types.html
Similar Messages
-
Associative Array (Object) problems
Here is the function i'm dealing with
i'm reading in a delimited string and using indexed arrays to
break them up and assign the keys and values to an associative
array in a loop.
i'm using variables in the loop and the array loads as
expected in the loop
but outside the loop, the only key is the variable name and
the value is undefined
this is true using dot or array notation, as well as literal
strings for the keys
any help is appreciated
watchSuspendData = function (id, oldval, newval):String {
//the incoming suspendData string is delimited by the
semicolon;
//newval is: firstValue=Yes;captivateKey=1
var listSuspendData:Array = newval.split(";"); // convert it
to a list of key/value pairs
if (listSuspendData.length > 0){
//line 123: listSuspendData.length is: 2
for (i=0; i < listSuspendData.length; i++){ //for each
key/value pair
var keyValArray:Array = new Array();
var myNameValue:String = listSuspendData
//line 127: listSuspendData is: firstValue=Yes
keyValArray = myNameValue.split("="); // split 'em on the
equal sign
var myKey:String = keyValArray[0];
var myVal:String = keyValArray[1];
//keyValArray[0] is: firstValue
//keyValArray[1] is: Yes
// store the key and the value in associative array
suspendDataArray.myKey = myVal;
trace("line 134: suspendDataArray is: " +
suspendDataArray.myKey);
// trace is line 134: suspendDataArray is: Yes on the first
pass and 1 on the second
//the below loop always returns one array key: myKey and the
value as undefined
for(x in suspendDataArray){
trace("x is: " + x); //x is: myKey
trace("the val is: " + suspendDataArray.x); //the val is:
undefined
} //end for
return newval;on lines 12-13 i assign the key=value pair to string
variables
then on lines 17-18 i assign those values to the associative
array using dot notation
the trace seems to work there
the problem is that when the procedure exits the for loop,
the associative array only has one key (myKey) and no value
(undefined)
all the documentation i've read shows using these types of
arrays with either non-quoted property names like:
myAssocArray.myKey = "somevalue";
or
myAssocArray[myKey] = "somevalue";
i tried assigning the key/value pairs directly from the
indexed arrays, but the result was always undefined
like this:
suspendDataArray.keyValArray[0] = keyValArray[1]
or
suspendDataArray[keyValArray[0]] = keyValArray[1]
i even tried building a string in the loop and trying to
assign all the pairs at once using the curly brace
this is pretty wierd behavior for actionscript or i'm missing
something basic here
thanks for looking -
Associative Array problem in Oracle Procedure
Hi,
I've searched through the internet and this forum and haven't been able to resolve a problem using associative array values within an IN clause. Everything I've read states that I can't use the associative array directly in the SQL statement. I have to convert it to a table and then I can use it. Unfortunately, I'm receiving an "ORA-21700: object does not exist or is marked for delete" error when trying to access the table I've populated from the array. Please note that I have verified the table is actually being populated during the loop. I'm catching the error when referencing it in the SELECT statement.
I've declared the following in the ARCHIVE package specification:
TYPE RSType IS REF CURSOR;
TYPE integer_aat IS TABLE OF INTEGER INDEX BY PLS_INTEGER;
TYPE integer_table IS TABLE OF INTEGER;
The procedure is as follows:
PROCEDURE SEL_SEARCH_RESULTS (v_term IN VARCHAR2,
v_categories IN ARCHIVE.integer_aat,
rs OUT RSType)
AS
/* PURPOSE: Return Search Results for the Category and Keyword Provided
VARIABLES:
v_categories = Document Categories array
v_term = Keyword entered
rs = Result Set
tbl_cat ARCHIVE.integer_table := ARCHIVE.integer_table();
BEGIN
FOR i IN 1 .. v_categories.COUNT
LOOP
tbl_cat.EXTEND(1);
tbl_cat(i) := v_categories(i);
END LOOP;
OPEN rs FOR
SELECT A.ID,
B.CATEGORY,
A.FILENAME,
A.DISPLAY_NAME,
A.COMMENTS
FROM TBL_ARCHIVE_DOCUMENTS A,
TBL_ARCHIVE_DOC_CAT B,
TBL_ARCHIVE_DOC_KEYWORDS C
WHERE A.ID = B.ID
AND A.ID = C.ID
AND B.CATEGORY IN (SELECT * FROM TABLE(tbl_cat))
AND C.KEYWORD = v_term
ORDER BY A.ID;
END SEL_SEARCH_RESULTS;
Any help would be greatly appreciated and thanks in advance,
MattThank you for the quick response. I looked at the example you suggested and made the following changes. Now I'm receiving an "Invalid datatype" error on the "SELECT column_value FROM TABLE(CAST(tbl_cat AS tbl_integer))" statement. I must be missing something simple and I just can't put my finger on it.
PROCEDURE SEL_SEARCH_RESULTS (v_term IN VARCHAR2,
v_categories IN ARCHIVE.integer_aat,
rs OUT RSType)
AS
/* PURPOSE: Return Search Results for the Category and Keyword Provided
VARIABLES:
v_categories = Document Categories array entered
v_term = Keyword entered
rs = Result Set
TYPE tbl_integer IS TABLE OF INTEGER;
tbl_cat tbl_integer;
BEGIN
FOR i IN 1 .. v_categories.COUNT
LOOP
tbl_cat.EXTEND(1);
tbl_cat(i) := v_categories(i);
END LOOP;
OPEN rs FOR
SELECT A.ID,
B.CATEGORY,
A.FILENAME,
A.DISPLAY_NAME,
A.COMMENTS
FROM TBL_ARCHIVE_DOCUMENTS A,
TBL_ARCHIVE_DOC_CAT B,
TBL_ARCHIVE_DOC_KEYWORDS C
WHERE A.ID = B.ID
AND A.ID = C.ID
AND B.CATEGORY IN (SELECT column_value FROM TABLE(CAST(tbl_cat AS tbl_integer)))
AND C.KEYWORD = v_term
ORDER BY A.ID;
END SEL_SEARCH_RESULTS; -
Would PL/SQL Associative Arrays solve my problem
Hi guys,
I'm new to PL/SQL development really, although i have some limited knowledge that i've relied on for the last couple of years to get by. Anyway, i'll try my best to describe my problem and how i'd like to solution it ...........
I have a table of information that holds a column for decscriptive names of payments and another column holding a difference value, for example :
employee_number | payment_name | difference
00001 | salary | 200.20
00001 | shift | 20.21
00002 | salary | 10.01
00002 | shift | 5.02
00003 | salary | 15.02
00003 | shift | 4.00I'd like to manipulate the way this data is presented, via DBMS_OUTPUT in a summary fashion counting the number of differences between ranges, for example :
payment_name | 0.00 to 10.00 | 10.01 to 100.00 | 100.01 to 99999.99
salary | | 2 | 1
shift | 2 | 1 |I thought it might be possible to use an approach in PL/SQL to mimic this table structure and populate it via a cursor looping through my initial recordset and posting the total count in the associated columns as required. Once the cursor had finished populating the PL/SQL table / array etc i could base my DBMS_OUTPUT on this data.
Or am i completely barking up the wrong tree, would there be a better, more efficient way to solution the problem. I've been reading up on PL/SQL Collections http://docs.oracle.com/cd/B10501_01/appdev.920/a96624/05_colls.htm#19661 but can't really determine is this is a) the correct approach or b) should i try using an associative array, nested table or varray?
Thanks in advance guys, just need a pointer in the right direction.It sounds like you can just pivot the data
SELECT payment_name,
SUM( CASE WHEN difference BETWEEN 0 and 10 THEN 1 ELSE 0 END ) "0.00 to 10.00",
SUM( CASE WHEN difference BETWEEN 10.01 and 100 THEN 1 ELSE 0 END ) "10.01 to 100.00",
SUM( CASE WHEN difference BETWEEN 100.01 and 99999.99 THEN 1 ELSE 0 END ) "100.01 to 99999.99"
FROM table_name
GROUP BY payment_nameYou can then do whatever you want with the data this query returns.
Justin -
Associative Array with subsripts(Index) as Varchar2
Hi All,
I m using Oracle Version 10.1.0.2.0 - Production
I have the following requirement:
The Associative array which holds the value with the subscripts(Index) as database table column.
Create table Period_master
Period_code_c Varchar2(10),
Period_frm_dt Date,
Period_to_dt Date,
Year_code_c Varchar2(10)
Insert into period_master values ('10',to_date('01/01/2012','dd/mm/rrrr'),to_date('31/01/2012','dd/mm/rrrr'),'2011-2012');
Insert into period_master Values ('11',to_date('01/02/2012','dd/mm/rrrr'),to_date('29/02/2012','dd/mm/rrrr'),'2011-2012');
Insert into period_master Values ('12',to_date('01/03/2012','dd/mm/rrrr'),to_date('31/03/2012','dd/mm/rrrr'),'2011-2012');
Insert into period_master Values ('13',to_date('01/04/2012','dd/mm/rrrr'),to_date('30/04/2012','dd/mm/rrrr'),'2012-2013');
Insert into period_master Values ('14',to_date('01/05/2012','dd/mm/rrrr'),to_date('31/05/2012','dd/mm/rrrr'),'2012-2013');
Commit;
SQL > Select * from Period_Master;
Period_code --- Period_frm_dt --- Period_to_dt ---- Year_code_c
10 --- 01/01/2012 --- 31/01/2012 --- 2011-2012
11 --- 01/02/2012 --- 29/02/2012 --- 2011-2012
12 --- 01/03/2012 --- 31/03/2012 --- 2011-2012
13 --- 01/04/2012 --- 30/04/2012 --- 2012-2013
14 --- 01/05/2012 --- 31/05/2012 --- 2012-2013
My Requirement is get the Period_frm_dt,period_end_dt and yearcode based on period_code (which is input parameters from my procedure) by using Collections.
I have to create one PLSQL table type which having the subscripts(Index) as period_code which is Varchar2 type;
I have written follwing code ,but it's not giving the desired output:
Declare
iv_period Varchar2(10);
Cursor cur_prd(cp_period Varchar2) is select * from Period_Master Where Period_code_c = cp_period;
TYPE PRD_REC_TY IS TABLE OF cur_prd%ROWTYPE INDEX BY PLS_INTEGER;
lv_prd_data prd_rec_ty;
lv_prd PERIOD_MASTER.period_code_c%TYPE ;
lv_frm_dt PERIOD_MASTER.Period_frm_dt%TYPE ;
lv_to_dt PERIOD_MASTER.Period_to_dt%TYPE ;
lv_yr_code PERIOD_MASTER.Year_code_c%TYPE ;
Begin
iv_period := :period;
Open Cur_prd(iv_period);
Loop
Fetch cur_prd BULK COLLECT into lv_prd_data;
EXIT WHEN cur_prd%NOTFOUND;
End Loop;
Close Cur_Prd;
If lv_prd_data.COUNT > 0 THEN
For i IN lv_prd_data.FIRST .. lv_prd_data.LAST
LOOP
lv_prd := lv_prd_data(i).pERIOD_cODE_C;
lv_frm_dt := lv_prd_data(i).Period_frm_dt;
lv_to_dt := lv_prd_data(i).Period_to_dt;
lv_yr_code := lv_prd_data(i).Year_Code_c;
Dbms_output.Put_line('For Period code : '||lv_prd||' the Year code is : '||lv_yr_code);
Dbms_output.Put_line('For Period code : '||lv_prd||' the Period_from_dt is : '||lv_frm_dt);
Dbms_output.Put_line('For Period code : '||lv_prd||' the Period_to_dt is : '||lv_to_dt);
END LOOP;
End if;
Exception
When Others Then
Dbms_output.Put_line('Here Error Found: '||SQLERRM);
End;
But My requirement is to get the FRM_DT,TO_DT and YEAR CODE as per the following:
For Period Code :*11* -- the YearCode is --- *2011-2012*
For Period Code :*11* -- the From Dt is --- *01/02/2012*
For Period Code :*11* -- the To Dt is --- *29/02/2012*
for Period Code : *14* -- the Yearcode is --- *2012-2013*
For Period Code : *14* -- the From Dt is --- *01/05/2012*
For Period Code : *14* -- the To Dt is --- *31/05/2012*
So on...
Like:
lv_yr_code := lv_period_data(iv_period).Year_code_c;
lv_frm_dt := lv_period_data(iv_period).Period_frm_dt;
lv_to_dt := lv_period_data(iv_period).Period_to_dt;
Dbms_output.Put_line('For Period code : '||iv_period||' the Year code is : '||lv_yr_code);
Dbms_output.Put_line('For Period code : '||iv_period||' the Period_from_dt is : '||lv_frm_dt);
Dbms_output.Put_line('For Period code : '||iv_period||' the Period_to_dt is : '||lv_to_dt);
How do i resolve the above scenario.Please help me to resove the above scenario.
Regards,
PrasantaHi, Pransanta,
Prasanta wrote:
... My Requirement is get the Period_frm_dt,period_end_dt and yearcode based on period_code (which is input parameters from my procedure) by using Collections.Sorry, I don't understand.
What is the porocedure you mentioned? Do you mean the anonymous block that you posted? If not, post the procedure. How is it related to the anonymous block? E.g., does the anonymous block need to call the procedure?
I have to create one PLSQL table type which having the subscripts(Index) as period_code which is Varchar2 type;
I have written follwing code ,but it's not giving the desired output:
Declare
iv_period Varchar2(10);Please format your code, and use \ tags to keep the formatting when you post it on this site.
See the forum FAQ {message:id=9360002}
Cursor cur_prd(cp_period Varchar2) is select * from Period_Master Where Period_code_c = cp_period;You're only looking for a single given period_code_c. If you want to get all rows, lose the WHERE clause. If you want to multiple rows, but not all rows, then use an appropriate WHERE clause.
TYPE PRD_REC_TY IS TABLE OF cur_prd%ROWTYPE INDEX BY PLS_INTEGER;
lv_prd_data prd_rec_ty;
lv_prd PERIOD_MASTER.period_code_c%TYPE ;
lv_frm_dt PERIOD_MASTER.Period_frm_dt%TYPE ;
lv_to_dt PERIOD_MASTER.Period_to_dt%TYPE ;
lv_yr_code PERIOD_MASTER.Year_code_c%TYPE ;
Begin
iv_period := :period;Post the code that declares and sets :period.
Open Cur_prd(iv_period);
Loop
Fetch cur_prd BULK COLLECT into lv_prd_data;
EXIT WHEN cur_prd%NOTFOUND;
End Loop;
Close Cur_Prd;
If lv_prd_data.COUNT > 0 THEN
For i IN lv_prd_data.FIRST .. lv_prd_data.LAST
LOOP
lv_prd := lv_prd_data(i).pERIOD_cODE_C;
lv_frm_dt := lv_prd_data(i).Period_frm_dt;
lv_to_dt := lv_prd_data(i).Period_to_dt;
lv_yr_code := lv_prd_data(i).Year_Code_c;If the block is just supposed to do what it's doing now; then you don't need all these local variables. It's simpler just to teference lv_prd_data.
If you're planning to add some other code to the block later, then the local variables could be useful.
Dbms_output.Put_line('For Period code : '||lv_prd||' the Year code is : '||lv_yr_code);
Dbms_output.Put_line('For Period code : '||lv_prd||' the Period_from_dt is : '||lv_frm_dt);
Dbms_output.Put_line('For Period code : '||lv_prd||' the Period_to_dt is : '||lv_to_dt);
END LOOP;
End if;
Exception
When Others Then
Dbms_output.Put_line('Here Error Found: '||SQLERRM);Only use an EXCEPTION section when you need to. The EXCEPTION section above is only hiding some information about the error.
End;
But My requirement is to get the FRM_DT,TO_DT and YEAR CODE as per the following:
For Period Code :*11* -- the YearCode is --- *2011-2012*
For Period Code :*11* -- the From Dt is --- *01/02/2012*
For Period Code :*11* -- the To Dt is --- *29/02/2012*
for Period Code : *14* -- the Yearcode is --- *2012-2013*
For Period Code : *14* -- the From Dt is --- *01/05/2012*
For Period Code : *14* -- the To Dt is --- *31/05/2012*
So on...
Like:
lv_yr_code := lv_period_data(iv_period).Year_code_c;
lv_frm_dt := lv_period_data(iv_period).Period_frm_dt;
lv_to_dt := lv_period_data(iv_period).Period_to_dt;
Dbms_output.Put_line('For Period code : '||iv_period||' the Year code is : '||lv_yr_code);
Dbms_output.Put_line('For Period code : '||iv_period||' the Period_from_dt is : '||lv_frm_dt);
Dbms_output.Put_line('For Period code : '||iv_period||' the Period_to_dt is : '||lv_to_dt);
How do i resolve the above scenario.Please help me to resove the above scenario.
Regards,
PrasantaIf the problem is that you need to show all period_code_cs, not just one, then you can do this:DECLARE
CURSOR cur_prd
IS SELECT *
FROM period_master
ORDER BY period_code_c;
TYPE prd_rec_ty IS TABLE OF cur_prd%ROWTYPE INDEX BY PLS_INTEGER;
lv_prd_data prd_rec_ty;
BEGIN
OPEN cur_prd;
FETCH cur_prd BULK COLLECT into lv_prd_data;
CLOSE cur_prd;
IF lv_prd_data.COUNT > 0
THEN
FOR i IN lv_prd_data.FIRST .. lv_prd_data.LAST
LOOP
dbms_output.put_line ( 'For Period code : '
|| lv_prd_data(i).period_code_c
|| ' the Year code is : '
|| lv_prd_data(i).year_code_c
dbms_output.Put_line ( 'For Period code : '
|| lv_prd_data(i).period_code_c
|| ' the Period_from_dt is : '
|| lv_prd_data(i).period_frm_dt
dbms_output.put_line ( 'For Period code : '
|| lv_prd_data(i).period_code_c
|| ' the Period_to_dt is : '
|| lv_prd_data(i).period_to_dt
END LOOP;
END IF;
END; -
Parsing XMLType into an associative array
I'm try to build a procedure that can take an XMLType as a parameter. What I would like to do be able to extract both the node names and the string values from each tag of the XML and put them into basically a key --> value associative array.
Ex:
<envelope>
<node1>value1</node1>
<node2>value2</node2>
</envelope>
Would be put into an array structured like: array['node1'] = 'value1', array['node2'] = 'value2', etc.
I'm not entirely sure how I go about looping through the XMLType and extracting each node name and its value. I'm not worried about repeating tag names.. I know that will not be an issue in any of the XMLTypes that I'm using.
Would anyone be able to offer any suggestions? If not how to go about doing exactly that, then at least some insight as to how I can loop through and extract nodes and/or values without specifying the specific XPaths?
Thanks in advance!Hi Roger,
This is a good description of your process. Just add your Oracle DB Version somewhere. Want you want is a method to map your XML to a relationship design (some special variant of object-relational-modelling).
I still see two choices:
The programmatic approach (as you described):
Have multiple procedures where you input an XML. Each procedure is reflecting a different part of the transformation process. So it depends from the Message which procedure you will call.
Example
SQL*Plus: Release 9.2.0.2.0 - Production on Thu Aug 14 17:53:25 2008
Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.
Connected to:
Oracle9i Enterprise Edition Release 9.2.0.8.0 - 64bit Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.8.0 - Production
SQL> create table testtab (username varchar2(100));
Table created.
/* the following insert part should be in some packaged procedure, */
/* this just don't work on my 9i version, but should in 10g */
SQL> insert into testtab (username)
2 select substr(extractvalue(column_value,'*/text()'),1,100)
3 from table(xmlsequence(extract( xmltype('<envelope>'||
4 '<username>Roger</username>'||
5 '<username>Sven</username>'||
6 '</envelope>'),'envelope/username')));
2 rows created.
SQL> select * from testtab;
USERNAME
Roger
Sven
SQL> The declarative approach:
Insert the XML into a SchemaBased Table. The XML Schema will provide the mapping to your relational tables automagically. Problem here is the writing, handling and evolving of the XML Schema (Sub schemas are allowed too). -
Associative Array our only option?
Hello,
I'm having a problem accepting associative arrays as the only option I have for getting data from a stored procedure. I have a good reason for not wanting to use ref cursors as I am using the stored procedure to manipulate data which I in turn would like to pass back to VB through the stored procedure and would rather not have to insert he data into a table just to re-select it for a ref cursor.
My main concern is that with associative arrays I am expected to define the number of return results before I even generate the data. Also from what I can see I am required to set the data length for each and every item in said array one at a time. All this overhead seems like more work than what I would have to do to utilizer a reference cursor. Is there a right way to do this? I would really like to do the most straight forward way I can without the extra processing.Hi,
Here's a blog post of mine that illustrates using pipelined functions and PL/SQL to return results:
http://oradim.blogspot.com/2007/10/odpnet-tip-using-pipelined-functions.html
Not sure if that will be helpful in your case, but perhaps it might be a place to start anyway.
- Mark -
Associative array comparison and INSERT upon IF condition
Hi Guys,
I have written this pl sql code to identify non existing sellers and insert their sales channel information into the dimension table (dimensional table update).
Somehow,......nothing is inserted and this script runs for 12 hours+ without any result. the sql autotrace shows no result and the explain plan (button on sql developer throws upon clicking "missing keyword". I have no
information what is going on/wrong. Does anyone spot an error?
UNDEFINE DimSales;
UNDEFINE FactTable;
DEFINE DimSales = 'testsales';
DEFINE FactTable = 'testfact';
DECLARE
v_SellerNo VarChar(9);
v_error_code T_ERRORS.v_error_code%TYPE;
v_error_message T_ERRORS.v_error_message%TYPE;
TYPE assoc_array_str_type1 IS TABLE OF VARCHAR2(32) INDEX BY PLS_INTEGER;
v1 assoc_array_str_type1;
TYPE assoc_array_str_type2 IS TABLE OF VARCHAR2(32) INDEX BY PLS_INTEGER;
v2 assoc_array_str_type2;
BEGIN
--Collect all distinct SellerNo into associative array (hash table)
select distinct SellerNo bulk collect into v1 from &FactTable;
select distinct seller_id bulk collect into v2 from &DimSales;
v_SellerNo := v1.first;
loop
exit when v1 is null;
--1 Check if v_SellerNo already exists in DIM_Sales (if NOT/FALSE, its a new seller and we can insert all records for that seller
if (v2.exists(v_SellerNo)=false) THEN
INSERT INTO &DimSales (K_Sales,REG,BVL,DS, VS,RS,GS,VK)
(SELECT DISTINCT trim(leading '0' from RS||GS) ,REG BVL,DS,VS,RS,GS,VK from &FactTable where SellerNo =v_SellerNo);
--ELSE
end if;
v_SellerNo := v1.next(v_SellerNo);
end loop;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
--v_error_code := SQLCODE
--v_error_message := SQLERRM
--INSERT INTO t_errors VALUES ( v_error_code, v_error_message);
END;
---------------------------------------------------------------Distinct clause requires a sort. Sorts can be very expensive.
Bulk collects that are not constrained in fetch size, can potentially fetch millions of rows - requiring that data to be wholly read into server memory. I have seen how this can degrade performance so badly that the kernel reboots the server.
Using PL/SQL loops to process and insert/update/delete data is often problematic due to its row-by-row approach - also called slow-by-slow approach. It is far more scalable letting SQL do the "loop" processing, by using joins, sub-selects and so on.
Where the conditional processing is too complex for SQL to handle, then PL/SQL is obviously an alternative to use. Ideally one should process data sets as oppose to rows in PL//SQL. Reduce context switching by using bulk fetches and bulk binds.
But PL/SQL cannot execute in parallel as the SQL it fires off can. If after all the optimisation, the PL/SQL process still needs to hit a million rows to process, it will be slow irrespective of how optimal that PL/SQL approach and design - simply because of the number of rows and the processing overheads per row.
In that case, the PL/SQL code itself need to be parallelised. There are a number of ways to approach this problem - the typical one is to create unique and distinct ranges of rows to process, spawn multiple P/SQL processes, and provide each with a unique range of rows to process. In parallel.
So you need to look close at what you are trying to achieve, what the workloads are, and how to effectively decrease the workloads and increase the processing time of a workload.
For example - finding distinct column values. You can pay for that workload when wanting that distinct list. And each time afterward repeat that workload when wanting that distinct list. Or you can pay for that workload up-front with the DML that creates/updates those values - and use (for example) a materialised view to maintain a ready to use distinct list of values.
Same workload in essence - but paying once for it and up-front as oppose to each time you execute your code that needs to dynamically build that distinct list.
Kent Crotty did tests and showed stunning performance improvements with bulk collect and forall, up to 30x faster:Bulk processing is not a magical silver bullet. It is a tool. And when correctly use, the tool does exactly what it was designed to do.
The problem is using a hammer to drive in screws - instead of a screwdriver. There's nothing "stunning" about using a screwdriver. It is all about using the correct tool.
If the goal of the swap daemon is to free up "idle" chunks of memory, and try to use that memory for things like file cache instead, what does that have to do with bulk processing?The swap daemon reads virtual memory pages from swap space into memory, and writes virtual pages from memory to swap space.
What does it have to do with bulk processing? A bulk fetch reads data from the SGA (buffer cache) into the PGA (private process memory space). The larget the fetch, the more memory is required. If for example 50% of server memory is required for a bulk collection that is 2GB in size, then that will force in-use pages from memory to swap space.. only to be swapped back again as it is needed, thereby forcing other in-use pages to swap. The swap daemon will consume almost all the CPU time swapping hot pages continually in and out of memory. -
I have a SELECT query like
SELECT ename, sal from emp;
I want the ename column values from the output of the above query to be populated into an Associative array. So i did the following steps
DECLARE
TYPE ename_type IS TABLE OF VARCHAR2(10) INDEX BY PLS_INTEGER;
v_ename ename_type;
What is the next step by which i could populate the v_ename variable with the ename column values from the above query?One slight problem.
The ename is derived from a large SELECT query joining three tables. The SELECT query looks like
SELECT ename,max(sal) from ...............;
I only need ename values from this SELECT query to be populated to the Assosiative array (v_ename). But I cannot exclude max(sal) from the query due to aggregate function's technical reasons(GROUP BY and so on).
So when i try to populate the associative array v_ename by issuing the following command:
SELECT ename BULK COLLECT INTO v_ename FROM (SELECT ename,max(sal) from ...............) ;
The max(sal) value in the SELECT query is going to be a problem. How do i solve this? -
Associative array two variable comparison :update table error
Hi,
i am using associative array to update the version number of table
-i declare two associative array to compare the column from two table (temp,main tables)values in loop
-if id's of both variable(for temp and main table) are equal and column of either of table not matching then it should update the version no of temp table
-if id's not equal i.e record only exists temp table then update null version number annd increment it by 1
-following is the structure and procedure --it shows the matching and non matching records but for the update statement it not giving expected output i.e it updates all records irrespective of the condition provided
-i tried to put condition in update as well as in the if statement but it updates all record
....suggestion and help highly appreciate. thankx in advance !!!
/*--table structure and data--*/
CREATE TABLE "TEMP_TABLE"
( "ID" NUMBER NOT NULL ENABLE,
"COL1" VARCHAR2(20 BYTE),
"COL2" VARCHAR2(20 BYTE),
"VERSION" NUMBER
INSERT INTO TEMP_TABLE VALUES (101,'A','B',NULL);
INSERT INTO TEMP_TABLE VALUES (102,'x','y',NULL);
INSERT INTO TEMP_TABLE VALUES (103,'r','t',NULL);
CREATE TABLE "MAIN_TABLE"
( "ID" NUMBER NOT NULL ENABLE,
"COL1" VARCHAR2(20 BYTE),
"COL2" VARCHAR2(20 BYTE),
"VERSION" NUMBER
INSERT INTO MAIN_TABLE VALUES (101,'A','B',1);
/*------update version procedure----------*/
DECLARE
TYPE T_tmp_table IS TABLE OF tmp_table %ROWTYPE INDEX BY PLS_INTEGER;
TYPE T_main_table IS TABLE OF main_table%ROWTYPE INDEX BY PLS_INTEGER;
l_tmp_table T_tmp_table;
l_main_table T_main_table;
BEGIN
SELECT * BULK COLLECT INTO l_tmp_table FROM tmp_table;
SELECT * BULK COLLECT INTO l_main_table FROM main_table;
FOR i IN 1 .. l_tmp_table.count
LOOP
FOR j IN 1 .. l_main_table.count
LOOP
if(l_tmp_table(i).ID = l_main_table(j).ID AND l_tmp_table(i).VERSION IS NULL) then
---this first if loop updates temp table version irrespective of l_tmp_table.ID=l_main_table.ID orl_tmp_table. ID<>l_main_table.ID .it display proper matching and non-matching records.
dbms_output.put_line('matching ids from tmp and main are :'||l_tmp_table(i).ID||' '||l_main_table(j).ID);
UPDATE tmp_table SET VERSION = l_tmp_table(i).version +1;
--where l_tmp_table(i).ID = l_main_table(j).ID --
end if;
if (l_tmp_table(i).ID <> l_main_table(j).ID) then
dbms_output.put_line('non matching ids from tmp and main are :'||l_tmp_table(i).ID||' '||l_main_table(j).ID);
UPDATE tmp_table SET VERSION = l_tmp_table(i).version +1;
--where l_tmp_table(i).ID <> l_main_table(j).ID
end if;
END LOOP;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('OTHER');
END;Hello user8937641
I think there is a problem with the logic of your SQL-Code.
But maybe in this formatted structure you can see where your problem is. -> I can not say it because I do not know what is the requirement.
I hope it helps...
/*------update version procedure----------*/
DECLARE
TYPE T_tmp_table IS TABLE OF tmp_table %ROWTYPE INDEX BY PLS_INTEGER;
TYPE T_main_table IS TABLE OF main_table%ROWTYPE INDEX BY PLS_INTEGER;
l_tmp_table T_tmp_table;
l_main_table T_main_table;
BEGIN
SELECT * BULK COLLECT INTO l_tmp_table FROM tmp_table;
SELECT * BULK COLLECT INTO l_main_table FROM main_table;
FOR i IN 1 .. l_tmp_table.count
LOOP
FOR j IN 1 .. l_main_table.count
LOOP
IF l_tmp_table(i).ID = l_main_table(j).ID
AND l_tmp_table(i).VERSION IS NULL
THEN
---this first if loop updates temp table version irrespective of l_tmp_table.ID=l_main_table.ID orl_tmp_table. ID<>l_main_table.ID .it display proper matching and non-matching records.
dbms_output.put_line('matching ids from tmp and main are :'||l_tmp_table(i).ID||' '||l_main_table(j).ID);
UPDATE tmp_table
SET version = l_tmp_table(i).version +1;
WHERE id = l_tmp_table(i).ID
END IF;
IF l_tmp_table(i).ID <> l_main_table(j).ID
THEN
dbms_output.put_line('non matching ids from tmp and main are :'||l_tmp_table(i).ID||' '||l_main_table(j).ID);
-- Maybe you do not need this update:
UPDATE tmp_table
SET version = l_tmp_table(i).version +1;
WHERE id = l_tmp_table(i).ID
END IF;
COMMIT;
END LOOP;
END LOOP;
EXCEPTION WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error at Executing the PLSQL-Block - ' || SQLERRM);
END; -
How to share an associative array throughout the entire instance?
Hi all,
I have an associative array with much data, and it would barely change, I want to keep it in the memory and share it throughout the entire instance, and make sure all sessions of all users get the same associative array, is it possible?
Thanks in advanced.>
I want to keep it in the memory and share it throughout the entire instance, and make sure all sessions of all users get the same associative array, is it possible?
Why do you want to keep it in memory?
Will using a table for that, which being accessed frequently, will practically always be in the buffer cache, cause performance problems for you?
The only other way to keep data in memory globally accesable to all database sessions is to use the global application context feature. With that you can store [name;value] pairs in memory and easily access (read) them using the SYS_CONTEXT built-in.
Toon -
How to use Associative Array in sql query?
Hello,
I have a problem on using Associative Array variable on query; and my query is similar to the one below;
TYPE OTHERGENERICS IS TABLE OF NUMBER(10) INDEX BY BINARY_INTEGER;
othersGenerics OTHERGENERICS;
CURSOR cursor_othersGenerics IS
select master.GENERICCODEID
from ASMTRG_ARTICLEMASTER master
join ASMTRG_ARTICLEMAP map on MAP.ARTICLECODEID = MASTER.ID
group by MASTER.GENERICCODEID
minus
select FGG.GENERICCODEID
from asmtrg_icfocusgroup fg
join asmtrg_icfocusgrpchannel fgc on FGC.GROUPID = FG.ID and fgc.isactive=1
join asmtrg_icfocusgengroup fgg on FGG.GROUPID = FG.ID and FGg.ISACTIVE=1
where fgc.channelid=1 and fg.isactive = 1
group by FGG.GENERICCODEID;
BEGIN
OPEN cursor_othersGenerics;
FETCH cursor_othersGenerics BULK COLLECT INTO othersGenerics;
CLOSE cursor_othersGenerics;
SELECT icfrd.*,
CASE
WHEN EXISTS(select ta.genericcodeid from <???XXX???> ta where ta.genericcodeid = icfgrp.genericcodeid) THEN -1
ELSE icfrd.icfgroupid
END CLASSIFICATION
FROM ASMTRGVIW_ICFOCUSREPORTDATA icfrd
LEFT JOIN ASMTRG_ICFOCUSGROUP icfgrp on icfrd.ICFGROUPID = icfgrp.ID
WHERE (channelId IS NULL OR icfrd.CHANNELID = channelId)
AND (asmCodeId IS NULL OR icfrd.ASMCODEID = asmCodeId)
AND (yearId IS NULL OR icfrd.YEARID = yearId)
AND (monthId IS NULL OR icfrd.MONTHID = monthId)
END;
By the way this is a part of my function.
The "othersGenerics" is my associative array variable and <???XXX???> is the place where I need to use my "othersGenerics" array to check. So far I've tried
"select ta.genericcodeid from table(cast(otherGenerics as OTHERGENERICS)) ta where ta.genericcodeid = icfgrp.genericcodeid",
"select ta.genericcodeid from table(otherGenerics) ta where ta.genericcodeid = icfgrp.genericcodeid",
"select ta.genericcodeid from otherGenerics ta where ta.genericcodeid = icfgrp.genericcodeid"
and these are not working.
What is your suggestions?Your type will have to be created as an independent object in your schema. So CREATE TYPE cannot be in your function declaration, it has to be outside it. (And then the line inside your declaration that defines the type will have to be removed.)
But now I can see that you use this array as a kind of "temporary lookup table" - you populate the array and then use it for lookup in your select statement. An alternative way of doing this could be like this completely without arrays:
with othergenerics as (
select master.GENERICCODEID
from ASMTRG_ARTICLEMASTER master
join ASMTRG_ARTICLEMAP map on MAP.ARTICLECODEID = MASTER.ID
group by MASTER.GENERICCODEID
minus
select FGG.GENERICCODEID
from asmtrg_icfocusgroup fg
join asmtrg_icfocusgrpchannel fgc on FGC.GROUPID = FG.ID and fgc.isactive=1
join asmtrg_icfocusgengroup fgg on FGG.GROUPID = FG.ID and FGg.ISACTIVE=1
where fgc.channelid=1 and fg.isactive = 1
group by FGG.GENERICCODEID
SELECT icfrd.*,
CASE
WHEN EXISTS(select ta.genericcodeid from othergenerics ta where ta.genericcodeid = icfgrp.genericcodeid) THEN -1
ELSE icfrd.icfgroupid
END CLASSIFICATION
FROM ASMTRGVIW_ICFOCUSREPORTDATA icfrd
LEFT JOIN ASMTRG_ICFOCUSGROUP icfgrp on icfrd.ICFGROUPID = icfgrp.ID
WHERE (channelId IS NULL OR icfrd.CHANNELID = channelId)
AND (asmCodeId IS NULL OR icfrd.ASMCODEID = asmCodeId)
AND (yearId IS NULL OR icfrd.YEARID = yearId)
AND (monthId IS NULL OR icfrd.MONTHID = monthId)
...The with clause (subquery factoring) you can think of as a kind of temp table called othergenerics created "on-the-fly".
The optimizer may decide to actually create a temp table for you and use in the lookup, or it may decide to rewrite the query into suitable joins or nested loops or hashing - whatever the optimizer decides will be the optimal way of doing things :-)
So unless you use your array other places in your function, I would recommend dropping the array completely, skip populating an array, and instead use a with clause for your temporary lookup. -
I am using a couple associative arrays in my code and comparing the data in one, and if it is an asterisk, I change it to use the data in the other. Here is the meat of my code. I am running into an error at the bolded line saying I have too many values, which I don't understand because the code is the exact same as the block of code right before it where I populate the first array. FYI, the table it is pulling from only has one row. The error is listed below the code.
Code
DECLARE
TYPE refresh_file_t IS TABLE OF test.loading_dock%ROWTYPE ;
refresh_data refresh_file_t ;
prospect_data refresh_file_t ;
TYPE CV_TYPE IS REF CURSOR ;
c_id CV_TYPE ;
v_id NUMBER(10) ;
v_phone VARCHAR2(10) ;
v_project VARCHAR2(10) ;
BEGIN
OPEN c_id FOR
'SELECT id
FROM test.loading_dock
WHERE rownum = 1' ;
LOOP
FETCH c_id INTO v_id ;
EXIT WHEN c_id%NOTFOUND ;
SELECT * BULK COLLECT
INTO refresh_data
FROM test.loading_dock
WHERE id = v_id ;
SELECT * BULK COLLECT
INTO prospect_data
FROM test.prospects
WHERE id_number = v_id ;
IF refresh_data(1).home_phone = '*' THEN
v_phone := prospect_data(1).phone ;
ELSE
v_phone := refresh_data(1).home_phone ;
END IF ;
DBMS_OUTPUT.PUT_LINE(v_phone) ;
END LOOP ;
CLOSE c_id ;
END ;
Error
ORA-06550: line 29, column 13:
PL/SQL: ORA-00913: too many values
ORA-06550: line 27, column 13:
PL/SQL: SQL Statement ignored
ORA-06550: line 34, column 46:
PLS-00302: component 'PHONE' must be declared
ORA-06550: line 34, column 13:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"Collection prospect_data is of type refresh_file_t, which is a table of records of test.loading_dock%TYPE. Most likely tables test.loading_dock and test.prospects have different structure - test.prospects has fewer columns. So when you try to fetch from test.prospects into prospect_data you get the error Try replacing
prospect_data refresh_file_t ;with
TYPE prospect_data_t IS TABLE OF test.prospects%ROWTYPE ;
prospect_data prospect_data_t ;SY. -
Hi All,
I've searched through this forum trying to find information I'm needing on associative arrays with a varchar2 index without luck. What I'm looking for is a way to get the index or "key" values of the array without knowing what they are. Meaning, I wouldn't have to know the index value when designing the array but would be able to utilize them values at runtime. For those familiar with Java it would be like calling the keySet() method from a Map object.
So, if I have an array of TYPE COLUMN_ARRAY IS TABLE OF VARCHAR2(4000) INDEX BY VARCHAR2(100) is there any way to dynamically get the index values without knowing what they are?
Any help is appreciated.
ThanksThanks for the response.
I am aware of using FIRST and NEXT for iterating the array but can I extract the index value of the current element into a variable when I don't know what the index value is at runtime ?
Thanks -
Associative Array vs Table Scan
Still new to PL/SQL, but very keen to learn. I wondered if somebody could advise me whether I should use a collection (such as an associative array) instead of repeating a table scan within a loop for the example below. I need to read from an input table of experiment data and if the EXPERIMENT_ID does not already exist in my EXPERIMENTS table, then add it. Here is the code I have so far. My instinct is that it my code is inefficient. Would it be more efficient to scan the EXPERIMENTS table only once and store the list of IDs in a collection, then scan the collection within the loop?
-- Create any new Experiment IDs if needed
open CurExperiments;
loop
-- Fetch the explicit cursor
fetch CurExperiments
into vExpId, dExpDate;
exit when CurExperiments%notfound;
-- Check to see if already exists
select count(id)
into iCheckExpExists
from experiments
where id = vExpId;
if iCheckExpExists = 0 then
-- Experiment ID is not already in table so add a row
insert into experiments
(id, experiment_date)
values(vExpId, dExpDate);
end if;
end loop;Except that rownum is assigned after the result set
is computed, so the whole table will have to be
scanned.really?
SQL> explain plan for select * from i;
Explained.
SQL> select * from table( dbms_xplan.display );
PLAN_TABLE_OUTPUT
Plan hash value: 1766854993
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 910K| 4443K| 630 (3)| 00:00:08 |
| 1 | TABLE ACCESS FULL| I | 910K| 4443K| 630 (3)| 00:00:08 |
8 rows selected.
SQL> explain plan for select * from i where rownum=1;
Explained.
SQL> select * from table( dbms_xplan.display );
PLAN_TABLE_OUTPUT
Plan hash value: 2766403234
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 1 | 5 | 2 (0)| 00:00:01 |
|* 1 | COUNT STOPKEY | | | | | |
| 2 | TABLE ACCESS FULL| I | 1 | 5 | 2 (0)| 00:00:01 |
Predicate Information (identified by operation id):
1 - filter(ROWNUM=1)
14 rows selected.
Maybe you are looking for
-
Email Notification not working Oracle BPEL 10.1.3.1.0
I'm not able to send out email notification using Email process activity from Component Palette. These are the steps that I've followed: 1. Email/SMTP server configuration details entered in the <SOA_oracle_home>\bpel\system\services\config\ns_emails
-
How to add convert traditional chinese / simplied chinese button?
After installing Microsoft Office 2010 Proofing Tools Kit Service Pack 1 (SP1), the button of Traditional Chinese to Simplied Chinese or vice versa still cannot be seen in the "Review" of Word. How can I install it?
-
SWF at 100 % width and proportional height
I have a SWF that I would like to use in an XHTML 1.0 Strict document. My need is that the flash must: - Be 100% width - Grow / shrink proportionally in height (depending on width) - The flash may not be cropped or distorted - I must be able to to pu
-
Suggestions for TM+CCC in a 160GB external disk
Hi, I am new to macs and to this wonderful discussion foruns. Just to make sure I will have no surprises in the future, I wanted to run in a newly purchased 160GB USB external disk both Time Machine and a bootable clone of the system (size is about 5
-
ALV : How to activate List - Export - Spreadsheet
Hi all, I have tried to search this forum n google regarding on the above problem but couldn't find any answer. Therefore, i have decided to post this question here. I have ALV report but somehow i couldn't Export -> spreadsheet directly. It was disa