id invalid column in cursor though my table has column id - plsql

My table has a defined column id.
Though the same query runs fin e independently,but when I try to declare it in a cursor, it shows invalid column.
declare
type PA_1 is record (PI number);
calc number;
row_container PA_1;
begin
for row_container in
(
select distict t1.pi , t2.id
from table1 t1, table2 t2
where t1.Pi=t2.pi
);
Loop
select calculation to calc
from table1 t1
where t1.pi=row_container.pi and t2.id=row_container.id;
end loop;
commit;
end;
the inner query runs fine otherwise. Please help

Several syntax errors:
First of all, you should remove the declaration for row_container. for row_container in () makes this inplicitely (and your declaration with one column doesn't even match your query with two columns).
distict should be distinct.
Then remove the semicolon before the Loop keyword. It doesn't belong there.
Then select calculation to calc should be select calculation into calc.
Inside the loop you select from table1 (which you call t1 again), but your where clause contains t2.id, while there is no t2 in that query.
And then: What is this routine supposed to do? It selects some value into the variable calc, but doesn't use it. So once it runs, it just doesn't do anything.

Related

Sqlite Update with Select subquery only chosing first value

I'm having a difficult time getting the UPDATE command in Sqlite to update all records with a new value it finds in another table using a subquery select statement. It incorrectly updates every column in Table1 with the first value it finds in column Table2.
When I run the select portion of the query alone it runs fine and returns all the proper values. It basically looks up the nearest value in multiples of 300000 from a lookup table called Adjustment.
code:
Update TEMP1
set New_position =
(
select (Temp1.Col1 + Adjustment.Offset) as NewValue
from Adjustment, TEMP1
where Adjustment.LookupValue = cast(TEMP1.Col1 / 300000 as Int) * 300000
)
You are trying to use a correlated subquery:
A SELECT statement used as either a scalar subquery or as the right-hand operand of an IN, NOT IN or EXISTS expression may contain references to columns in the outer query. Such a subquery is known as a correlated subquery. A correlated subquery is reevaluated each time its result is required. An uncorrelated subquery is evaluated only once and the result reused as necessary.
But your subquery does not actually contain a reference to columns in the outer query; the FROM Temp1 in the inner query is a new instance of the Temp1 table, and Temp1.Col1 refers to that. The value of Temp1.Col1 is from some random row in Temp1.
Just drop the Temp1 from the FROM clause:
UPDATE Temp1
SET New_position = (SELECT Temp1.Col1 + Adjustment.Offset
FROM Adjustment
WHERE LookupValue = CAST(Temp1.Col1 / 300000 AS INT) * 300000);

PL/SQL List of items, check if record exists, if yes update if not create

Im trying to learn PL/SQL and I was given an assignment which I am not sure how to tackle.
I am given a list of orders. I want to check my ORDER table for each of them in the following way:
Check if order exists, if no create a record
Check if order fullfilled (0 or 1)
If order is not fullfilled (0), update to 1
I put together a script which I think can do this for one order, but I'm sure it's not very good:
DECLARE
tmp NUMBER;
tmp2 NUMBER;
o_id NUMBER := 999;
BEGIN
/*Checking if order exists */
SELECT COUNT (*)
INTO tmp
FROM ORDERS
WHERE ORDERID = o_id;
IF ( tmp = 0 ) THEN
/* INSERT HERE */
END IF;
SELECT FULLFILLED INTO tmp2
FROM ORDERS
WHERE ORDERID = o_id;
IF (tmp2 = 0) THEN
/* UPDATE... */
END IF;
end;
I would appreciate any advice, what should I look into to make this script efficient? Thank you.
MERGE statement is what you need. It is based on SELECT statement and let's you UPDATE or INSERT data using it's WHEN (NOT) MATCHED THEN clauses. Here's a good explanation with some examples: Oracle Base MERGE Statement.
Here's also some code snippet you might find useful:
DECLARE
o_id NUMBER := 999;
BEGIN
MERGE INTO ORDERS o
USING
(SELECT o_id AS orderid FROM dual) o_id
ON
(o.orderid = o_id.orderid)
WHEN MATCHED THEN
UPDATE SET
o.fulfilled = CASE WHEN o.fulfilled = 0 THEN 1 ELSE o.fulfilled END
WHEN NOT MATCHED THEN
INSERT (fulfilled, <some_other_columns>)
VALUES (1, <values_for_other_columns>);
END;
/
Please read up on the merge statement: https://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_9016.htm
Also called an "upsert". Basically if the row does not exist, insert. If it does, update.
It does what you are trying to do in one statement.

PLSQL FOR loop while executing CURSOR

I would like to know if there's any option to iterate a table while performing SELECT values into a CURSOR.
For example:
I have a table TEMP_NUMBERS which contains only numbers (single column).
I have to perform a SELECT from each number in the table (I do not know the amount of rows in the table in advance).
Here is basically what I'm attempting to do. Obviously this does not work, but can I do some kind of a workaround?
I need to SELECT the data into the p_cv_PermsNotifs which is a RETURN REF CURSOR.
IF NOT p_cv_PermsNotifs%ISOPEN THEN OPEN p_cv_PermsNotifs FOR
FOR i IN 1..TEMP_NUMBERS.NUMBER.COUNT LOOP
SELECT DISTINCT
SEC_USER_ROLE.ENTITY_TYP_CODE,
SEC_USER_ROLE.ENTITY_ID
FROM
SEC_USER_ROLE
WHERE
SEC_USER_ROLE.ENTITY_ID = i
END LOOP;
END IF;
Also tried this:
IF NOT p_cv_PermsNotifs%ISOPEN THEN OPEN p_cv_PermsNotifs FOR
SELECT DISTINCT
SEC_USER_ROLE.ENTITY_TYP_CODE,
SEC_USER_ROLE.ENTITY_ID
FROM
SEC_USER_ROLE
WHERE
SEC_USER_ROLE.ENTITY_ID IN
(SELECT * FROM TABLE (lv_ListOfEntities))
END IF;
Where lv_ListOfEntities is table of NUMBER indexed by BINARY INTEGER.
But I'm getting "ORA-22905: cannot access rows from a non-nested table item"
Thanks in advance.
In> Hey if you pass a single number at a time, everytime the refcursor
will be overwritten by the next value. So at the end you will only get
the value for last number in the refcursor. A better way is to use
some basic PL/SQL Bulk COLLECT logic which will give you the desired
output.
Hope this helps
--Creating sql type
CREATE OR REPLACE TYPE lv_num_tab IS TABLE OF NUMBER;
--plsql block
var p_lst refcursor;
DECLARE
lv_num lv_num_tab;
BEGIN
SELECT COL1 BULK COLLECT INTO lv_num FROM TEMP_NUMBERS;
OPEN p_lst FOR
SELECT DISTINCT SEC_USER_ROLE.ENTITY_TYP_CODE,
SEC_USER_ROLE.ENTITY_ID
FROM SEC_USER_ROLE
WHERE SEC_USER_ROLE.ENTITY_ID IN
(SELECT * FROM TABLE(cast(lv_num as lv_num_tab))
);
END;

Insert into Table with the first column being a Sequence

I am trying to use an Insert, Sequence and Select * to work together.
INSERT INTO BRK_INDV
Select * from (Select brk_seq.NEXTVAL as INDV_SEQ, a.*
FROM (select to_date(to_char(REQUEST_DATETIME,'DD-MM-YYYY'),'DD-MM-YYYY') BUSINESS_DAY, to_char(REQUEST_DATETIME,'hh24') src_hour,
CASE tran_type
WHEN 'V' THEN 'Visa'
WHEN 'M' THEN 'MasterCard'
ELSE tran_type
end text,
tran_type, count(*) as count
from DLY_STATS
where 1=1
AND to_date(to_char(REQUEST_DATETIME,'DD-MM-YYYY'),'DD-MM-YYYY') = '09-FEB-2015'
group by to_date(to_char(REQUEST_DATETIME,'DD-MM-YYYY'),'DD-MM-YYYY'),to_char(REQUEST_DATETIME,'hh24'),tran_type order by src_hour)a);
This gives me the following error:
ERROR at line 2:
ORA-02287: sequence number not allowed here
I tried to remove the order by and still the same error.
However, if I only run
Select brk_seq.NEXTVAL as INDV_SEQ, a.*
FROM (select to_date(to_char(REQUEST_DATETIME,'DD-MM-YYYY'),'DD-MM-YYYY') BUSINESS_DAY, to_char(REQUEST_DATETIME,'hh24') src_hour,
CASE tran_type
WHEN 'V' THEN 'Visa'
WHEN 'M' THEN 'MasterCard'
ELSE tran_type
end text,
tran_type, count(*) as count
from DLY_STATS
where 1=1
AND to_date(to_char(REQUEST_DATETIME,'DD-MM-YYYY'),'DD-MM-YYYY') = '09-FEB-2015'
group by to_date(to_char(REQUEST_DATETIME,'DD-MM-YYYY'),'DD-MM-YYYY'),to_char(REQUEST_DATETIME,'hh24'),tran_type order by src_hour)a;
It shows me proper entries. Then, why is select * not working for that?
Kindly help.
I see what you're trying to do. You want to insert rows into the BRK_INDV table in a particular order. The sequence number, which I assume will be the primary key of BRK_INDV, will be generated sequentially in the sorted order of the input rows.
You are working with a relational database. One of the first characteristics we all learn about a relational database is that the order of the rows in a table is insignificant. That's just a fancy word for fugitaboutit.
You cannot assume that a select * from table will return the rows in the same order they were written. It might. It might for quite a long time. Then something -- the number of rows, the grouping of some column values, the phase of the moon -- something will change and you will get them out in a seemingly totally random order.
If you want order, it must be imposed in the query, not the insert.
Here's the statement you should be executing:
INSERT INTO BRK_INDV
With
Grouped( Business_Day, Src_Hour, Text, Tran_Type, Count )As(
Select Trunc( Request_Datetime ) Business_Day,
To_Char( Request_Datetime, 'hh24') Src_Hour,
Case Tran_Type
When 'V' Then 'Visa'
When 'M' Then 'MasterCard'
Else Tran_Type
end Text,
Tran_Type, count(*) as count
from DLY_STATS
Where 1=1 --> Generated as dynamic SQL?
And Request_Datetime >= Date '2015-02-09'
And Request_Datetime < Date '2015-02-10'
Group By Trunc( Request_Datetime ), To_Char( Request_Datetime, 'hh24'), Tran_Type
)
Select brk_seq.Nextval Indv_Seq, G.*
from Grouped G;
Notice there is no order by. If you want to see the generated rows in a particular order:
select * from Brk_Indv order by src_hour;
Since there could be hundreds or thousands of transactions in any particular hour, you probably order by something other than hour anyway.
In Oracle, the trunc function is the best way to get a date with the time portion stripped away. However, you don't want to use it in the where clause (or, aamof, any other function such as to_date or to_char)as that would make the clause non-sargable and result in a complete table scan.
The problem is that you can't use a sequence in a subquery. For example, this gives the same ORA-02287 error you are getting:
create table T (x number);
create sequence s;
insert into T (select * from (select s.nextval from dual));
What you can do, though, is create a function that returns nextval from the sequence, and use that in a subquery:
create function f return number as
begin
return s.nextval;
end;
/
insert into T (select * from (select f() from dual));

PL/SQL - LAST_VALUE return more than one row?

I am doing an school assigment where I need to get the last value of "code" so I can then insert next row with this code incremented. I tried to pull it out this way.
DECLARE
v_last_code f_shifts.code%TYPE;
BEGIN
SELECT LAST_VALUE(code) OVER (ORDER BY code)
INTO v_last_code
FROM f_shifts;
DBMS_OUTPUT.PUT_LINE('Last value is: ' || v_last_code);
END;
However I get ORA-01422: exact fetch returns more than one requested number of rows
and I have no idea why and how can a last_value be more than one row
Thanks !
You can use a nested table like this.
DECLARE
v_last_code f_shifts.code%TYPE;
TYPE t_tbl IS TABLE OF f_shifts.code%TYPE;
-- Above line creates the nested table type of the required type.
v_tbl T_TBL;
-- Above line creates the nested table variable.
BEGIN
SELECT code
BULK COLLECT INTO v_tbl -- collects all the values, ordered, into the nested table
FROM f_shifts
ORDER BY code;
v_last_code = v_tbl(v_tbl.LAST); -- gets the last values and puts into the variable
DBMS_OUTPUT.PUT_LINE('Last value is: ' || v_last_code);
END;
/

Resources