Oracle Using Variable in DML data fetch? - sql

I'm trying the following:
SET SERVEROUTPUT ON;
DECLARE
V_COUNT NUMBER := 1;
V_LIITM NUMBER := 1;
V_LIMCU CHAR(12) := NULL;
V_LILOCN CHAR(20) := NULL;
V_LILOTN CHAR(30) := NULL;
BEGIN
-- INITIAL COUNT --
SELECT COUNT(*) INTO V_COUNT FROM QADTA.F41021
where exists (select IBGLPT FROM QADTA.F4102
where ibitm = liitm and ibmcu = LIMCU
and ibglpt <> liglpt AND (LIPBIN = 'P' OR LIPQOH != 0));
DBMS_OUTPUT.PUT_LINE('There are initially '||V_COUNT||' missing registries.');
-- GENERATING SEARCH VARIABLES --
SELECT LIITM, LIMCU, LILOCN, LILOTN, LIGLPT
INTO V_LIITM, V_LIMCU, V_LILOCN, V_LILOTN, V_LIGLPT
FROM QADTA.F41021 where exists (select IBGLPT FROM QADTA.F4102
where ibitm = liitm and ibmcu = LIMCU and ibglpt <> liglpt
AND (LIPBIN = 'P' OR LIPQOH != 0)) AND ROWNUM <= 1;
-- FIRST EXERCISE -- INSERT --
INSERT INTO QADTA.F41021_BACKOUT
SELECT LIITM, LIMCU, LILOCN, LILOTN, LIGLPT FROM QADTA.F41021
WHERE (LIITM, LIMCU, LILOCN, LILOTN)
IN (V_LIITM, V_LIMCU, V_LILOCN, V_LILOTN);
-- SECOND EXERCISE -- UPDATE --
UPDATE QADTA.F41021
SET F41021.LIGLPT = (SELECT IBGLPT FROM QADTA.F4102
WHERE IBITM = LIITM and IBMCU = LIMCU)
WHERE (LIITM, LIMCU, LILOCN, LILOTN)
IN (V_LIITM, V_LIMCU, V_LILOCN, V_LILOTN);
END;
However, when executing I get error "00920 - Invalid Relational Operators". I nullified the DML sentences & it worked, but I need to do data modification.

With IN operator you can check if a list of expressions exists in another list of expressions. So if you use multiple expressions to the left of the operator, you also have to use multiple expressions on the right side.
In your example you will try to compare 4 values LIITM, LIMCU, LILOCN, LILOTN with the values V_LIITM, V_LIMCU, V_LILOCN, V_LILOTN.
For it to work, you have to make a group of elements by putting them in parentheses.(V_LIITM, V_LIMCU, V_LILOCN, V_LILOTN).
WHERE (LIITM, LIMCU, LILOCN, LILOTN)
IN ((V_LIITM, V_LIMCU, V_LILOCN, V_LILOTN)
, (V_LIITM, V_LIMCU, V_LILOCN, V_LILOTN));
Without the parentheses, Oracle interprets the four values V_LIITM, V_LIMCU, V_LILOCN, V_LILOTN as a list of elements, and they do not match to the expression.

This is wrong:
WHERE (LIITM, LIMCU, LILOCN, LILOTN)
IN (V_LIITM, V_LIMCU, V_LILOCN, V_LILOTN);
Have a look at an example:
SQL> create table x as select * From dept where 1 = 2;
Table created.
SQL> insert into x
2 select * from dept
3 where (deptno, dname) in (10, 'ACCOUNTING');
where (deptno, dname) in (10, 'ACCOUNTING')
*
ERROR at line 3:
ORA-00920: invalid relational operator
SQL> insert into x
2 select * from dept
3 where deptno = 10
4 and dname = 'ACCOUNTING';
1 row created.
SQL>
I guess you see what you have to do in order to fix it.
Also, while generating search variables, you're selecting into V_LIGLPT which is not declared.
Furthermore, I'd suggest you to use table aliases and qualify all columns in all queries with those aliases, so that it is clear which column belongs to which table.
Finally, when INSERTING INTO, name all target columns - don't rely on the fact that you "know" description of the target table, as it may (and some day it will) change.

Related

ORA-01722: invalid number while select numeric column and same numeric column reference in where

select id
from abc
where id = 1001;
return invalid number.
abc is a view
datatype of id is number
In create view cast is used
CREATE OR REPLACE VIEW abc AS
SELECT CAST (SUBSTR(T_ID,3,100) AS NUMBER) AS ID
from TEST
id column has all numeric values only
SELECT * FROM ABC WORKS FINE AND SO insert query works
tried below code which returns nothing in dbms output
declare
l_dummy number;
begin
for cur in (select ID from abc)
loop
begin
l_dummy := to_number(cur.ID);
exception
when others then dbms_output.put_line(cur.ID);
end;
end loop;
end;
column datatype nullable
ID NUMBER Yes 1 NO NO NO
SELECT *
FROM abc
WHERE
ID = 1001
returns:
ORA-01722: invalid number
01722. 00000 - "invalid number"
*Cause: The specified number was invalid.
*Action: Specify a valid number.
There are two problems:
SUBSTR(T_ID,3,100) will not return number always -- We need to consider the only numeric query
If we apply anything in WHERE condition of the view, Order of the execution of the WHERE condition is the call of Oracle optimizer.
Please see below code:
-- Data preparation
create table TEST (T_ID varchar2(20));
INSERT INTO TEST VALUES('AB1000'); -- Good data
INSERT INTO TEST VALUES('AB1001'); -- Good data
INSERT INTO TEST VALUES('CD1001'); -- Good data
INSERT INTO TEST VALUES('XY1004'); -- Good data
INSERT INTO TEST VALUES('XYZ1004'); -- Bad data
--
-- Data in the table
SELECT * FROM TEST;
Output
-- You need to create your view as following
-- rownum is used so that WHERE clause of view is executed first
-- and then any external WHERE clause on the view is executed
CREATE OR REPLACE VIEW ABC_NEWVIEW AS
SELECT
ID
FROM
(
SELECT
CAST(SUBSTR(T_ID, 3, 100) AS NUMBER) AS ID,
ROWNUM RN
FROM
TEST
WHERE
CASE
WHEN TRIM(TRANSLATE(SUBSTR(T_ID, 3, 100), '0123456789-,.', ' ')) IS NULL THEN 'numeric'
ELSE 'alpha'
END = 'numeric'
)
--
-- View
SELECT * FROM ABC_NEWVIEW
Output
-- Query using WHERE condition
SELECT *
FROM ABC_NEWVIEW
WHERE
ID = 1001
Output
You can find the demo in the following link:
DB Fiddle demo
You can see the demo of VIEW without ROWNUM:
Reproduced your issue -- So the issue is the order of the execution.
Cheers!!

Nested SELECT statement in FROM clause

I want to get data from table which name is keeping in another table. Trying to get this as described below leads to getting result from nested SELECT only
select * from (select value from ex_scheme.ex_tab where name = 'ex_name.current_table_name')
I mean, I've got equivalent result as from just
select value from ex_scheme.ex_tab where name = 'ex_name.current_table_name'
query.
UPDATED
Ok, lets double-check if I was correctly understood.
I have to see one table data (lets call this table "table1"). I need to know this table name. And I know where its name is keeping. It is in another table (call it "names_table") in column "name" (row with column value = 'table1'). And I can get it by query
select name from names_table where value = 'table1'
If you know in advance the column and its type, you can build some dynamic SQL to dynamically query a table or another.
For example, say you have tables like the following:
create table table1(col) as (select 1 from dual);
create table table2(col) as (select 2 from dual);
create table tab_of_tabs (tab_name) as (select 'TABLE1' from dual);
You can use dynamic SQL to build a query that scans a table whose name is the result of a query:
SQL> declare
2 vSQL varchar2(1000);
3 vResult number;
4 begin
5 select 'select sum(col) from ' || tab_name -- build the query
6 into vSQL
7 from tab_of_tabs;
8 --
9 execute immediate vSQL into vResult; -- run the query
10 --
11 dbms_output.put_line('Result: ' || vResult);
12 end;
13 /
Result: 1
PL/SQL procedure successfully completed.
SQL>
If I understand correctly, you could use a nested query in a where clause. For example,
select * from table1 where table1.name in (select name from table2);
This assumes there's a column "name" in table1. The result of this query should return the rows in table1 that are in table2.
try giving alias
select n.* from (select value from ex_scheme.ex_tab where name = 'ex_name.current_table_name') n;
Update:
It is in another table (call it "names_table") in column "name" (row
with column value = 'table1').
this query will work
select n.* from (select name from ex_scheme.ex_tab where name = 'ex_name.current_table_name') n;
sub query fetches name of table from another table .

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.

Using SEQUENCE(Oracle) in WHERE Clause [duplicate]

The following Oracle SQL code generates the error "ORA-02287: sequence number not allowed here":
INSERT INTO Customer (CustomerID,Name) VALUES (Customer_Seq.nextval,'AAA');
SELECT * FROM Customer where CustomerID=Customer_Seq.currval;
The error occurs on the second line (SELECT statement). I don't really understand the problem, because this does work:
INSERT INTO Customer (CustomerID,Name) VALUES (Customer_Seq.nextval,'AAA');
SELECT Customer_Seq.currval from dual;
You have posted some sample code, so it is not clear what you are trying to achieve. If you want to know the assigned value, say for passing to some other procedure you could do something like this:
SQL> var dno number
SQL> insert into dept (deptno, dname, loc)
2 values (deptno_seq.nextval, 'IT', 'LONDON')
3 returning deptno into :dno
4 /
1 row created.
SQL> select * from dept
2 where deptno = :dno
3 /
DEPTNO DNAME LOC
---------- -------------- -------------
55 IT LONDON
SQL>
Edit
We can use the RETURNING clause to get the values of any column, including those which have been set with default values or by trigger code.
You don't say what version of Oracle you are using. There have in the past been limitations on where sequences can be used in PL/SQL - mostly if not all gone in 11G. Also, there are restrictions in SQL - see this list.
In this case you may need to write:
SELECT Customer_Seq.currval INTO v_id FROM DUAL;
SELECT * FROM Customer where CustomerID=v_id;
(Edited after comments).
This doesn't really directly answer your question, but maybe what you want to do can be resolved using a the INSERT's RETURNING clause?
DECLARE
-- ...
last_rowid rowid;
-- ...
BEGIN
-- ...
INSERT INTO Customer (CustomerID,Name) VALUES (Customer_Seq.nextval,'AAA') RETURNING rowid INTO last_rowid;
SELECT * FROM Customer where rowid = last_rowid;
-- ...
END;
/
You may not use a sequence in a WHERE clause - it does look natural in your context, but Oracle does not allow the reference in a comparison expression.
[Edit]
This would be a PL/SQL implementation:
declare
v_custID number;
cursor custCur is
select customerid, name from customer
where customerid = v_custID;
begin
select customer_seq.nextval into v_custID from dual;
insert into customer (customerid, name) values (v_custID, 'AAA');
commit;
for custRow in custCur loop
dbms_output.put_line(custRow.customerID||' '|| custRow.name);
end loop;
end;
You have not created any
sequence
First create any sequence its cycle and cache. This is some basic example
Create Sequence seqtest1
Start With 0 -- This Is Hirarchy Starts With 0
Increment by 1 --Increments by 1
Minvalue 0 --With Minimum value 0
Maxvalue 5 --Maximum Value 5. So The Cycle Of Creation Is Between 0-5
Nocycle -- No Cycle Means After 0-5 the Insertion Stopes
Nocache --The cache Option Specifies How Many Sequence Values Will Be Stored In Memory For Faster Access
You cannot do Where Clause on Sequence in SQL beacuse you cannot filter a sequence . Use procedures like #APC said

Regarding sql substitution

When i ran the below queries it's failing in the second query becuase prev_test_ref1 variable is not defined. If i remove the insert statement in the first query ,run again then it's working and using the prev_test_ref1 value from the first sql query in second query. Is it because of variable scope? How can i resolve this with the insert statement.
QUERY1
column prev_test_ref1 new_value prev_test_ref1 ;
insert into testing.test_ref_details(TEST_TYPE,TEST_REF_NO)
select '1',max(test_ref_no) as prev_test_ref1
from testing.test_runs_status
where test_type = 1
and run_status = 1
and test_end_dt = (select last_day(add_months(trunc(sysdate),-6))+2 from dual)
group by test_end_dt
;
QUERY2
column last_test_end_dt new_value last_test_end_dt;
select to_char(test_completion_dt,'DD-MON-YYYY HH24:MI:SS') as last_test_end_dt
from testing.test_runs_status
where test_ref_no = '&prev_test_ref1';
In SQLPlus substitution variables will only be defined with SELECT statements. Your first insert doesn't return rows so it won't work (think about it: it only returns 1 row inserted., SQLPlus has no way to know the value inserted.)
I suggest you add a step to save the value into the variable (or use a PL/SQL block):
column prev_test_ref1 new_value prev_test_ref1 ;
SELECT MAX(test_ref_no) AS prev_test_ref1
FROM testing.test_runs_status
WHERE test_type = 1
AND run_status = 1
AND test_end_dt = (SELECT last_day(add_months(trunc(SYSDATE), -6)) + 2
FROM dual)
GROUP BY test_end_dt;
INSERT INTO testing.test_ref_details(TEST_TYPE,TEST_REF_NO)
VALUES ('1', &prev_test_ref1);
SELECT ...
declare
prev_test_ref1 number(10);
begin
insert into ...select ...;
select ... into prev_test_ref1 from ...;
end;
/
The INSERT statement has a RETURNING clause. We can use this to get access to "unknown" values from the table. The following examples uses RETURNING to get the assigned nextval from a sequence, but we could return any column from the row:
SQL> var prev_id number
SQL> insert into t23 (id, name) values (my_seq.nextval, 'MAISIE')
2 returning id into :prev_id
3 /
1 row created.
SQL> select * from t23
2 where id = :prev_id
3 /
NAME ID
---------- ----------
MAISIE 122
SQL>
Unfortunately the RETURNING clause only works with single-row SQL.
It isn't really clear what the purpose of the whole script is, especially in light of the comment "i have similar sql query which returns multiple rows. In that case i cant have separate insert statement."
If you want to use the results of a select, see if Multi-Table Inserts fit the bill. Your select statement can insert into both the primary table and also a second table (eg a global temporary table). You can then query the global temporary table to see what rows were inserted.