ORA-01006: Bind Variable while Executing Select - oracle

During the Initialization block of a newly created package, I populate one field in a record stored in an associative array (Indexed by VARCHAR2). I then loop over the associative array to store the second field on each record. These records store a Table Name and the column name which the foreign key for a given table. (Our product has a generic form so I can later generate some SQL with the only difference being the table and column names). Here is some example code:
CREATE OR REPLACE PACKAGE BODY pk_example IS
TYPE pkt_TableInfo IS RECORD
(
BaseTable VARCHAR2(30),
FKColumn VARCHAR2(30)
);
TYPE pkt_TableInfoTable IS TABLE OF pkt_TableInfo INDEX BY VARCHAR2(30);
pk_tTableInfo pkt_TableInfoTable;
pk_sIndex VARCHAR2(30);
/***FUNCTION AND PROCEDURE DEFINITIONS***/
--Initialization Section
BEGIN
pk_tTableInfo('TABLE_A').BaseTable := 'TABLE_B';
pk_tTableInfo('TABLE_C').BaseTable := 'TABLE_D';
pk_sIndex := pk_tTableInfo.FIRST;
WHILE pk_sIndex IS NOT NULL LOOP
SELECT acc.column_name
INTO pk_tTableInfo(pk_sIndex).FKColumn
FROM all_constraints ac
INNER JOIN all_constraints ac2 ON ac.r_owner = ac2.owner AND ac.r_constraint_name = ac2.constraint_name
INNER JOIN all_cons_columns acc ON ac.owner = acc.owner AND ac.constraint_name = acc.constraint_name
WHERE ac.owner = sys_context('userenv', 'current_schema')
AND ac.constraint_type = 'R'
AND ac.table_name = pk_sIndex
AND ac2.table_name = pk_tTableInfo(pk_sIndex).BaseTable;
pk_sIndex := pk_tTableInfo.NEXT(pk_sIndex);
END LOOP;
END pk_example;
Everything compiles just fine, however, whenever I attempt to run a function from this package, I receive an "ORA-01006: bind variable does not exist" error pointing to the line "SELECT acc.column_name".
Now for the kicker. This works in my development environment but is failing when I try to run the package in QA.
I have all the same permissions and I can run the query just fine. In fact, if I replace pk_tTableInfo(pk_sIndex) with a string 'TABLE_B' the query runs just fine. (For the first pass through the loop...then I get No Data)
The foreign keys are the same between environments.
Both environments are running Oracle 12cR2.
Thank you for taking the time to read through this, I appreciate any input.

I've just bumped into the quite same issue. I have FOR..LOOP cursor and I'm getting ORA-01006 error on FOR line. Query is not dynamic. It was working perfectly on Oracle 11g but we upgraded Oracle to 19c version and this issue occured.
The only suspicious thing was that query used function in WHERE clause. Luckily it didn't reference any columns so I removed it from query and calculated before FOR..LOOP statement into a variable. After that it worked like magic.
In your case I would try to get rid of references to collection and use just scalar variables and then assign them to collection elements. The same goes for pk_tTableInfo(pk_sIndex).BaseTable value.

Related

How to duplicate rows with a column change for a list of tables?

Given the following example:
BEGIN
FOR r IN (
SELECT * FROM table_one WHERE change_id = 0
) LOOP
r.change_id := -1;
INSERT INTO table_one VALUES r;
END LOOP;
END;
This inserts new rows to table_one with the exact same content, except the intended change on column change_id to the value -1. I don't have to specify the columns inside of the script as I have to in an INSERT INTO table_one (change_id, ...) SELECT -1, ... FROM table_one WHERE change_id=0;
It works perfectly fine. But how to modify this script to work with a list of tables? The internal structure of those tables are different, but all of them have the necessary column change_id.
Of course the easiest solution would be to copy and paste this snippet x-times and replace the fix table name inside. But is there an option to work with a list of tables in an array?
My approach was like this:
DECLARE
TYPE tablenamearray IS VARRAY(30) OF VARCHAR2(30);
tablenames tablenamearray;
BEGIN
tablenames := tablenamearray('TABLE_ONE', 'TABLE_TWO', 'TABLE_THREE'); -- up to table 30...
FOR i IN tablenames.first..tablenames.last LOOP
/* Found no option to use tablenames(i) here with dynamic SQL */
END LOOP;
END;
Note: There is no technical primary key like an id with a sequence behind. The primary key is build by three columns incl. the change_id column.
You cannot create a SQL statement where the statement is not known at parse time. So, you cannot have a variable as a table name. What you're looking for is Dynamic SQL, which is a fairly complicated topic, but basically you're going to wind up building a SQL statement with DBMS_SQL or running a statement as a string with EXECUTE IMMEDIATE.

Oracle access varray elements in SQL

I'm playing around with array support in Oracle and hit a roadblock regarding array access within a SQL query. I'm using the following schema:
create type smallintarray as varray(10) of number(3,0);
create table tbl (
id number(19,0) not null,
the_array smallintarray,
primary key (id)
);
What I would like to do is get the id and the first element i.e. at index 1 of the array. In PostgreSQL I could write select id, the_array[1] from tbl t but I don't see how I could do that with Oracle. I read that array access by index is only possible in PL/SQL, which would be fine if I could return a "decorated cursor" to achieve the same result through JDBC, but I don't know if that's possible.
DECLARE
c1 SYS_REFCURSOR;
varr smallintarray2;
BEGIN
OPEN c1 FOR SELECT t.id, t.THE_ARRAY from tbl t;
-- SELECT t.THE_ARRAY INTO varr FROM table_with_enum_arrays2 t;
-- return a "decorated cursor" with varr(1) at select item position 1
dbms_sql.return_result(c1);
END;
You can do this in plain SQL; it's not pretty, but it does work. You would prefer that Oracle had syntax to hide this from the programmer (and perhaps it does, at least in the most recent versions; I am still stuck at 12.2).
select t.id, q.array_element
from tbl t cross apply
( select column_value as array_element,
rownum as ord
from table(the_array)
) q
where ord = 1
;
EDIT If order of generating the elements through the table operator is a concern, you could do something like this (in Oracle 12.1 and higher; otherwise the function can't be part of the query itself, but it can be defined on its own):
with
function select_element(arr smallintarray, i integer)
return number
as
begin
return arr(i);
end;
select id, select_element(the_array, 1) as the_array_1
from tbl
/
First of all, please don't do that on production. Use tables instead of storing arrays within a table.
Answer to your question is to use column as a table source
SELECT t.id, ta.*
from tbl t,
table(t.THE_ARRAY) ta
order by column_value
-- offset 1 row -- in case if sometime you'll need to skip a row
fetch first 1 row only;
UPD: as for ordering the array I can only say playing with 2asc/desc" parameters provided me with results I've expected - it has been ordered ascending or descending.
UPD2: found a cool link to description of performance issues might happen

How can use array type in where clause of update statement

I want a procedure get a list of disabling privilege, and update their record in table. For doing this scenario, I defined an array as database object with below code:
CREATE OR REPLACE TYPE T_DISABLE_LIST IS TABLE OF NUMBER(32)
Then I defined an input parameter in procedure signature, and got passed value.
PROCEDURE PRC_ROLE_PRIVILAGE_MANAGEMENT(P_REQ_USER_ID IN VARCHAR2,
P_DISABLE_LIST IN T_DISABLE_LIST,
P_RES_DESC OUT VARCHAR2)
BEGIN
UPDATE T_ PRIVILAGE p
SET P.ENABLE_STATUS = 0, P.GRANT_USERID = P_REQ_USER_ID
WHERE P.ID IN (SELECT * FROM TABLE(P_DISABLE_LIST));
COMMIT;
EXCEPTION
WHEN OTHERS THEN
RES_DESC := SUBSTR(SQLERRM,1, 400);
END;
This procedure will be compile successfully. But when I test it, I got this error:
ORA-22905: cannot access rows from a non-nested table item
Any body can help me? And say why this code don't work correctly?
And finally, how can i resolve this problem?
P.S: My orcale version is 9.2!!!
This assumes you are using Oracle 10g or later (and was written before the OP clarified what version they are using)
Use the MEMBER OF operator:
UPDATE T_PRIVILAGE
SET ENABLE_STATUS = 0,
GRANT_USERID = P_REQ_USER_ID
WHERE ID MEMBER OF P_DISABLE_LIST;
You can also use the COLUMN_VALUE pseudo-column:
UPDATE T_PRIVILAGE
SET ENABLE_STATUS = 0,
GRANT_USERID = P_REQ_USER_ID
WHERE ID IN ( SELECT COLUMN_VALUE FROM TABLE( P_DISABLE_LIST ) );
why this code does not work correctly?
SELECT * FROM TABLE( P_DISABLE_LIST )
Is selecting the row from the table. However, the table is generated by a table collection expression and there is no underlying database table to reference a row of so Oracle generates an ORA-22905 exception.(there would be an underlying table if the collection was stored in a nested table; which is why that situation is specifically mentioned in the exception).
Update: PL/SQL solution:
FOR i IN 1 .. P_DISABLE_LIST.COUNT LOOP
UPDATE T_PRIVILAGE
SET ENABLE_STATUS = 0,
GRANT_USERID = P_REQ_USER_ID
WHERE ID = P_DISABLE_LIST(i);
END LOOP;

PL/SQL procedure - too many values

I'm sure this is something simple, but I'm really new to PL/SQL and this has me stuck.
I've written a simple stored procedure to return a few values about a customer. Right off the bat, the %rowtype's are not coming up as reserved keywords but the compiler isn't flagging those as errors.
It is, however, ignoring the entire SQL statement flagging the line FROM demo_customers as too many values. Even if I try reducing it to only select one column it still gives me the same error.
create or replace
PROCEDURE GETCUSTOMER
(
arg_customerID demo_customers.customer_id%type,
returnRec OUT demo_customers%rowtype
)
AS
BEGIN
SELECT customer_id, cust_first_name, cust_last_name, cust_email
INTO returnRec
FROM demo_customers
WHERE customer_id = arg_customerID ;
END GETCUSTOMER;
If you want to select into a %ROWTYPE record, you'll want to do a SELECT * rather than selecting individual columns
create or replace
PROCEDURE GETCUSTOMER
(
arg_customerID demo_customers.customer_id%type,
returnRec OUT demo_customers%rowtype
)
AS
BEGIN
SELECT *
INTO returnRec
FROM demo_customers
WHERE customer_id = arg_customerID ;
END GETCUSTOMER;
If you select 4 columns explicitly, Oracle expects you to have 4 variables to select those values into.

Can a table variable be used in a select statement where clause?

I have a stored procedure that is doing a two-step query. The first step is to gather a list of VARCHAR2 type characters from a table and collect them into a table variable, defined like this:
TYPE t_cids IS TABLE OF VARCHAR2(50) INDEX BY PLS_INTEGER;
v_cids t_cids;
So basically I have:
SELECT item BULK COLLECT INTO v_cids FROM table_one;
This works fine up until the next bit.
Now I want to use that collection in the where clause of another query within the same procedure, like so:
SELECT * FROM table_two WHERE cid IN v_cids;
Is there a way to do this? I am able to select an individual element, but I would like to use the table variable like a would use a regular table. I've tried variations using nested selects, but that doesn't seem to work either.
Thanks a lot,
Zach
You have several choices as to how you achieve this.
If you want to use a collection, then you can use the TABLE function to select from it but the type of collection you use becomes important.
for a brief example, this creates a database type that is a table of numbers:
CREATE TYPE number_tab AS TABLE OF NUMBER
/
Type created.
The next block then populates the collection and performs a rudimentary select from it using it as a table and joining it to the EMP table (with some output so you can see what's happening):
DECLARE
-- Create a variable and initialise it
v_num_tab number_tab := number_tab();
--
-- This is a collection for showing the output
TYPE v_emp_tabtype IS TABLE OF emp%ROWTYPE
INDEX BY PLS_INTEGER;
v_emp_tab v_emp_tabtype;
BEGIN
-- Populate the number_tab collection
v_num_tab.extend(2);
v_num_tab(1) := 7788;
v_num_tab(2) := 7902;
--
-- Show output to prove it is populated
FOR i IN 1 .. v_num_tab.COUNT
LOOP
dbms_output.put_line(v_num_tab(i));
END LOOP;
--
-- Perform a select using the collection as a table
SELECT e.*
BULK COLLECT INTO v_emp_tab
FROM emp e
INNER JOIN TABLE(v_num_tab) nt
ON (e.empno = nt.column_value);
--
-- Display the select output
FOR i IN 1 .. v_emp_tab.COUNT
LOOP
dbms_output.put_line(v_emp_tab(i).empno||' is a '||v_emp_tab(i).job);
END LOOP;
END;
You can see from this that the database TYPE collection (number_tab) was treated as a table and could be used as such.
Another option would be to simply join your two tables you are selecting from in your example:
SELECT tt.*
FROM table_two tt
INNER JOIN table_one to
ON (to.item = tt.cid);
There are other ways of doing this but the first might suit your needs best.
Hope this helps.
--Doesn't work.
--SELECT item BULK COLLECT AS 'mySelectedItems' INTO v_cids FROM table_one;
SELECT table_two.*
FROM table_two INNER JOIN v_cids
ON table_two.paramname = v_cids.mySelectedItems;
Unless I'm misunderstanding the question, this should only return results that are in the table variable.
Note: I've never used Oracle, but I imagine this case would be the same.

Resources