With cluse in insert - oracle

Odd things going on with procedure below.
When I run procedure it insert into table only 125 records. When I insert data without procedure with the same parameters it gives me 15000 records. Do you have any ideas what could it be?
create or replace procedure calc_p(p_startdate number,
p_end_date number,
p_id number) is
begin
dbms_output.put_line('Start: ' || substr(localtimestamp, 1, 17));
delete rep_pd
where b_id = p_id
and rep_date between p_startdate and p_end_date;
-- commit;
insert into rep_pd
with cont
(rep_date, b_id, con_number, okres_zap) as
(select /*+ materialize*/
rep_date,
b_id,
con_number,
nvl(maturity_date - origin_date, 0) as okres_zap
from con#sg_al
where description not in ('OVER', 'Overn')
and b_id = p_id
and rep_date between p_startdate
and p_end_date)
select mr.suffix1,
mr.data_danych,
ma.rep_date,
b_id,
mr.k_symbol,
konto_nazwa,
case
when mr.k_nazwa likr '%EXT%' then
'04'
when mr.k_symbol like '3731%' then
'12'
else
mr.typ_k_symbol
end as cl_t,
ma.okres_zap,
mr.rezydent_symbol,
mr.w_symbol as cu,
mr.sal_ma_pl as outstanding,
get_pd_account(k_symbol) AS depo substr(to_number(to_char(data_danych, 'yyyymmdd')), 7, 2) as dzien
from abc.a_mr mr
left join cont ma
on ma.b_id = p_id
and to_date(ma.rep_date, 'YY/MM/DD') = mr.data_danych
and ma.con_number = mr.suffix1
where sal_ma_pl <> 0
and get_pd_account(k_symbol) is not null
and nvl(typ_k_symbol, '20') not in ('01', '02', '02A')
and mr.data_danych between to_date(p_startdate, 'YY/MM/DD') and
to_date(p_end_date, 'YY/MM/DD');
-- commit;
dbms_output.put_line('Koniec: ' || substr(localtimestamp, 1, 17));
end calc_p;

There are some errors or unadvised uses in your code:
When I copy-paste your code in my editor, I see an error. There is , missing between your depo and dzien columns in select.
Before anything else, double check that you gave us a correct code and that you haven't erased one part of the code. Also, double check that you haven't missed the same part of the code when transferring it in or out of the procedure.
As Littlefoot stated in the comment to your question, there should be no difference between INSERT stetements inside and outside stored procedures, if they are just being encapsulated with BEGIN END. There is one difference however, and that is the way you're defining your input parameters.
I see that you are defining your p_startdate and p_end_date as NUMBER parameters. Possible difference is that you're really expecting dates. The difference between the results of your insert inside and outside the procedure can easily be due to defining your p_startdate and p_end_date parameters as numbers.
If after these corrections you still get a difference in results, please provide us with some sample table data that is getting you that difference.

Related

pl sql insert into within a procedure and dynamic variables

I need some help with PL SQL. I have to insert some data into table. Another application is calling my procedure and caller is passing few details which I also need to insert into my table.
here is the syntax I am struggling with:
PROCEDURE invform_last2orders_item_insert( p_userId IN NUMBER
,p_accountId IN NUMBER
,p_site_Id IN NUMBER
,p_return_message OUT VARCHAR2) IS
Begin
insert into mytable
(p_userId , p_accountId , p_site_Id , sku, description, 'Cart', 1, unitId)
as
select sku, description, unitId
from mycatalogtable where site_id= p_site_Id ) ;
End;
Can you help me with syntax? I need to pass three parameters from called in parameter and some values returned from select query. How can I achieve this?
thank you for your help.
That would be something like this; see comments within code:
PROCEDURE invform_last2orders_item_insert
( p_userId IN NUMBER
,p_accountId IN NUMBER
,p_site_Id IN NUMBER
,p_return_message OUT VARCHAR2)
IS
Begin
insert into mytable
-- first name all columns you'll be inserting into; I don't know their
-- names so I just guessed
(userid,
accountid,
siteid,
sku,
description,
col1,
col2,
unitid
)
-- if you were to insert only values you got via parameters, you'd use the
-- VALUE keyword and insert those values separately.
-- As some of them belong to a table, use SELECT statement
(select p_userid,
p_accountid,
p_siteid,
c.sku,
c.description,
'Cart',
1,
c.unitid
from mycatalogtable c
where c.site_id = p_site_Id
);
-- I don't know what you are supposed to return; this is just an example
p_return_message := sql%rowcount || ' row(s) inserted';
End;
in your select statement you should have the same number of columns as you are inserting into the table, your code should be something like this example,
DECLARE
userid varchar2(20) := 'Jack';
Begin
INSERT INTO mytable (SELECT userid, SPORT from OLYM.OLYM_SPORTS);
commit;
end;

Update with "with data as" clause and regex do not commit. Why?

Sorry for the delay! I've taken a workload greater then i can handle these couple weeks.
Ok, lets clarify things up!
There is a proprietary software running on top of it and
I do not have de ability to change this software! Actually it allows me to do a few things.
In this specific case I can not create a relation 1>N, I can not create a new table! What I do can is create fields.
So, how do i customize things? Through the database! Using triggers, functions and procedures. I know its a bad "work around" but it's what i got and works flawlessly 99% of times. Any exception my code throws the application shows on the screen!
The problem is actually the update not working.
You ask:
Why you're looping when you're forcing there to only be one iteration (unless P_QTDLINHAS can be < 1 I suppose?)
R: Couse the user can select multiple lines in the application but I dont want them to do it. So a throw an error on the screen.
Why you have nested begin/end blocks.
R: Couse I may have to throw exceptions, I got used to write begin Statements Exception some message end.
Sample data:
CREATE TABLE SAMPLE_DATA
(
PKNUMBER NUMBER NOT NULL
, DESCRIPTION VARCHAR2(20)
, GROUPTOCHANGE VARCHAR2(100)
, STATUS VARCHAR2(1 BYTE)
, CONSTRAINT SAMPLE_DATA_PK PRIMARY KEY
(
PKNUMBER
)
ENABLE
);
INSERT INTO sample_data VALUES (1000,'ORDER1',NULL,NULL);
INSERT INTO sample_data VALUES (2000,'ORDER2',NULL,NULL);
INSERT INTO sample_data VALUES (3000,'ORDER3',NULL,NULL);
INSERT INTO sample_data VALUES (4000,'ORDER4','1000,2000,30001',NULL);
In this case the field GROUPTOCHANGE will be filled by the user like this "2108,8090,8843". Each number represents a PKNUMBER in same table "SAMPLE_DATA".
yes i know! the user can type something wrong.. let's ignore this for now!
The field STATUS will eventually be updated to 'C','L','R' OR NULL. When this happens I Need this logic to be executed:
IF OLD.STATUS <> NEW.STATUS AND GROUPTOCHANGE IS NOT NULL THEN
UPDATE SAMPLE_DATA SP
SET SP.STATUS = :NEW.STATUS
WHERE SP.PKNUMBER IN (:NEW.GROUPTOCHAGE)
AND PS.GROUPTOCHANGE IS NULL;
END IF;
Dispite the bad design is it possible to do?
Thank for any help!!
Here what I've done so far:
create or replace PROCEDURE "AD_LIBERA_FRETES_FILHOS"(
P_CODUSU NUMBER,
P_IDSESSAO VARCHAR2,
P_QTDLINHAS NUMBER,
P_MENSAGEM OUT VARCHAR2)
AS
P_NUNOTA NUMBER(10);
P_CONTROLE VARCHAR(100);
P_PEDIDOS VARCHAR(100);
P_STATUS VARCHAR(100);
BEGIN
-- avoid more than 1 at a time
IF (P_QTDLINHAS > 1) THEN
RAISE_APPLICATION_ERROR(-20000, 'SELECIONE APENAS UM PEDIDO PARA EXECUTAR ESTA AÇÃO.');
END IF;
FOR I IN 1..P_QTDLINHAS LOOP
--extract param from session
P_NUNOTA := ACT_INT_FIELD(P_IDSESSAO, I, 'PKNUMBER');
P_STATUS := ACT_TXT_FIELD(P_IDSESSAO, I, 'STATUS');
--verify typed text should be "84090,89830,83393..."
BEGIN
SELECT REGEXP_REPLACE(CAB.GROUPTOCHANGE, '[0-9-, ]', ''),
CAB.GROUPTOCHANGE
INTO P_CONTROLE,
P_PEDIDOS
FROM SAMPLE_DATA CAB
WHERE CAB.PKNUMBER = P_NUNOTA;
END;
IF (P_CONTROLE IS NOT NULL) THEN
RAISE_APPLICATION_ERROR(-20000, '<B> SOMETHING WRONG !</B>');
ELSE
--perform de update (not working)
BEGIN
UPDATE SAMPLE_DATA C
SET C.STATUS = P_STATUS
WHERE
C.GROUPTOCHANGE IS NULL AND
C.PKNUMBER IN
(WITH DATA AS
(SELECT CAB.GROUPTOCHANGE STR
FROM SAMPLE_DATA CAB
WHERE CAB.PKNUMBER = P_NUNOTA )
SELECT TRIM(REGEXP_SUBSTR(STR, '[^,]+', 1, LEVEL)) STR
FROM DATA CONNECT BY INSTR(STR, ',', 1, LEVEL - 1) > 0);
END;
END IF;
END LOOP;
--mgs to show
P_MENSAGEM := 'DONE!! CHECK -> '||P_PEDIDOS;
END;

Inserting into a table using a procedure only if the record doesn't exist yet

I have a table that i'm trying to populate via a plsql script (runs on plsql developer). The actual DML statement
is contained in a procedure inside a package. The procedure only inserts if the record doesn't exist yet.
It doesn't work. The part that checks for existence returns true after the first iteration of the script loop even if it doesn't actually exist in the table.
If i put the commit outside of the loop, nothing gets inserted at all and the existence checks return true for all iteration even if the table it empty.
When i try to simplify the insert with existence check to be in just one statement without the exception handling, i get the same outcome.
Please tell me what I'm doing wrong here.
CREATE OR REPLACE PACKAGE BODY some_package
IS
PROCEDURE add_to_queue(id IN NUMBER)
IS
pending_record VARCHAR2(1);
BEGIN
-- this part succeeds even if nothing matches the criteria
-- during the loop in the outside script
SELECT 'Y'
INTO pending_record
FROM dual
WHERE EXISTS (SELECT 'x' FROM some_queue smq
WHERE smq.id = id AND smq.status IS NULL);
EXCEPTION
WHEN NO_DATA_FOUND THEN
INSERT INTO some_queue (seqno, id, activity_date)
VALUES (some_sequence.nextval, id, SYSDATE);
WHEN OTHERS THEN
NULL;
END;
END some_package;
CREATE TABLE some_queue
(
seqno VARCHAR2(500) NOT NULL,
id NUMBER NOT NULL,
activity_date DATE NOT NULL,
status VARCHAR2(25),
CONSTRAINT some_queue_pk PRIMARY KEY (seqno)
);
-- script to randomly fill in the table with ids from another table
declare
type ids_coll_tt is table of number index by pls_integer;
ids_coll_table ids_coll_tt;
cursor ids_coll_cur is
select tab.id
from (select *
from ids_source_table
order by dbms_random.value ) tab
where rownum < 10;
begin
open ids_coll_cur;
fetch ids_coll_cur bulk collect into ids_coll_table;
close ids_coll_cur;
for x in 1..ids_coll_table.count
loop
some_package.add_to_queue(ids_coll_table(x));
commit; -- if this is here, the first iteration gets inserted
end loop;
-- commit; -- if the commit is done here, nothing gets inserted
end;
Note: I translated this code to be more generic for posting. Forgive me if there are any typos.
Update: even if i put everything inside the script and not use the package, i'm not able to properly check for existence and I get the same results.
I figured out the solution:
CREATE OR REPLACE PACKAGE BODY some_package
IS
PROCEDURE add_to_queue(p_id IN NUMBER)
IS
pending_record VARCHAR2(1);
BEGIN
-- this part succeeds even if nothing matches the criteria
-- during the loop in the outside script
SELECT 'Y'
INTO pending_record
FROM dual
WHERE EXISTS (SELECT 'x' FROM some_queue smq
WHERE smq.id = p_id AND smq.status IS NULL);
EXCEPTION
WHEN NO_DATA_FOUND THEN
INSERT INTO some_queue (seqno, id, activity_date)
VALUES (some_sequence.nextval, p_id, SYSDATE);
WHEN OTHERS THEN
NULL;
END;
END some_package;
changing the parameter name fixed it. I guess the compiler gets confused if it's the same name as the table field.
Don't name the parameter the same as the column (use a prefix like p_ or in_) and you can do it in a single statement if you use a MERGE statement self-joining on the ROWID pseudo-column:
CREATE OR REPLACE PACKAGE BODY some_package
IS
PROCEDURE add_to_queue(
in_id IN NUMBER
)
IS
BEGIN
MERGE INTO some_queue dst
USING ( SELECT ROWID AS rid
FROM some_queue
WHERE id = in_id
AND status IS NULL ) src
ON ( src.rid = dst.ROWID )
WHEN NOT MATCHED THEN
INSERT (seqno, id, activity_date)
VALUES (some_sequence.nextval, in_id, SYSDATE);
END;
END some_package;

Create a procedure to print the result of and SQL query

I'm using Oracle SQL developer and I want to create a procedure to print the result of this SQL code:
SELECT Title , Name
FROM BOOK, BORROWER, BOOK_LOANS
WHERE Due_date = SYSDATE AND Return_date = NULL;
Here's my code. I keep getting "SQL statement ignored, statement ignored, LOOP index variable 'books' is invalid". Please tell me what I am missing here. I tried moving the SQL statement to a cursor, but it also doesn't work.
CREATE OR REPLACE PROCEDURE overdueToday IS
BEGIN
FOR books IN (SELECT Title , Name
FROM BOOK, BORROWER, BOOK_LOANS
WHERE Due_date = SYSDATE
AND Return_date = NULL)
LOOP
DBMS_OUTPUT.put_line(books.Title || ' -- ' || books.Name);
END LOOP;
END overdueToday;
/
Apart from what you've already been told (cross-join), WHERE condition won't work. SYSDATE is a function that returns both date and time, so there's no chance that it'll return anything. You should use TRUNC function.
Moreover, when dealing with NULL values, they aren't "equal" (=) to anything - you should use IS NULL (or IS NOT NULL, depending on what you do).
The following example is kind of stupid; you didn't provide test case so I'm creating my own tables with absolutely minimal column set, just to be sure that the procedure won't fail.
SQL> create table book (title varchar2(20));
Table created.
SQL> create table borrower (name varchar2(20));
Table created.
SQL> create table book_loans (due_date date, return_date date);
Table created.
SQL>
SQL> insert into book values ('Pinky');
1 row created.
SQL> insert into borrower values ('Littlefoot');
1 row created.
SQL> insert into book_loans values (trunc(sysdate), null);
1 row created.
SQL>
The procedure; I marked places you should pay attention to:
SQL> set serveroutput on;
SQL> create or replace procedure overduetoday
2 is
3 begin
4 for books in ( select title,
5 name
6 from book,
7 borrower,
8 book_loans
9 where due_date = trunc(sysdate) --> trunc!
10 and return_date is null --> is!
11 ) loop
12 dbms_output.put_line(books.title ||' -- '|| books.name);
13 end loop;
14 end overduetoday;
15 /
Procedure created.
SQL>
SQL> exec overduetoday;
Pinky -- Littlefoot
PL/SQL procedure successfully completed.
SQL>
As you can see, it works (or, should I rather say, *doesn't fail". If there were more rows in those tables, the result would be really wrong).
Errors you mentioned can't be raised with code you posted. That's why it is important to post exactly what you do, just as I did. Doing so, there's no doubt in what you have, what you did and how Oracle responded. Anything else is just a matter of speculation.
Oracle 12c and above, you may use DBMS_SQL.RETURN_RESULT. You require proper join conditions and aliases in your query, which we are not aware of.
CREATE OR replace PROCEDURE OverdueToday
IS
rc SYS_REFCURSOR;
BEGIN
OPEN rc FOR
SELECT title, -- b.title ?
name -- br.name ?
FROM book b
join borrower br
ON ( 1 = 1 ) --Add proper join condition here
join book_loans bl
ON ( 1 = 1 ) --Add proper join condition here
WHERE due_date = TRUNC(SYSDATE)
AND return_date IS NULL;
DBMS_SQL.RETURN_RESULT(rc);
END overduetoday;
/
If you're using SQL Developer, you can just run your query (hit F5 or Ctrl+Enter). You don't need to write a PL/SQL program.
That being said, your query is almost certainly wrong since you have three tables and no join conditions for them. But you should still get some output.
This is the simple way to loop in select statement:
CREATE OR REPLACE PROCEDURE overdueToday IS
CURSOR book_cur is
SELECT Title , Name
FROM BOOK, BORROWER, BOOK_LOANS
WHERE Due_date = SYSDATE
AND Return_date = NULL;
BEGIN
FOR book_rec IN book_cur loop
DBMS_OUTPUT.put_line(book_rec .Title || ' -- ' || book_rec .Name);
END LOOP;
END overdueToday;

Code not working. It takes an eternity to run

The code below runs for an eternity.
As you can see i have to take values from one table and use that value to check if the second table contains it or not and insert into the third table values from the first table.
Is there any other way of doing this?
create or replace PROCEDURE KPI_AVAILABILITY (
v_programid varchar2
)
AS
v_MASTER_KPI_ID number;
v_UDF varchar2(100);
v_count number;
cursor c1 is
(select MASTER_KPI_ID,UDF from KPI_MASTER
where UDF is not null
and ISACTIVE = 1
--order by MASTER_KPI_ID,udf
);
BEGIN
open c1 ;
fetch c1 into v_MASTER_KPI_ID,v_UDF;
while v_UDF is not null
loop
select count(v_UDF) into v_count
from vw_ticket
where v_UDF is not null
and amsprogramid = v_programid;
if v_count is not null or v_count <> 0 then
delete from program_kpi where amsprogramid = v_programid;
INSERT INTO PROGRAM_KPI (AMSPROGRAMID,MASTER_KPI_ID,LASTUPDATEDBYDATALOAD)
VALUES(V_PROGRAMID,v_MASTER_KPI_ID,to_char(sysdate,'dd-mon-yy hh.mi.ss'));
dbms_output.put_line('xyz');
end if;
end loop;
close c1;
END KPI_AVAILABILITY;
Reverse engineering business rules from another developer's code is always tricky, especially without understanding the wider domain. However, at the centre of the loop is DELETE from program_kpi followed by an INSERT into the same table. If there are no records matching on amsprogramid = v_programid then you're inserting a record, if there are matches then effectively you're just updating lastupdatedbydataload with the current SYSDATE.
In others, it appears to be the logic of a MERGE. So perhaps your code could be entirely replaced with a single statement. If so, this is likely to be a lot more efficient than the row-by-agonizing-row process within a cursor loop.
merge into program_kpi pkpi
using (select kpim.master_kpi_id
, kpim.udf
, v_programid
from kpi_master kpim
where kpim.udf is not null
and kpim.isactive = 1
and exists ( select null
from vw_ticket tkt
where tkt.amsprogramid = v_programid)
) kpim
on (kpim.v_programid = pkpi.programid
and kpim.master_kpi_id = pkpi.master_kpi_id)
when not matched then
insert values (kpim.v_programid, kpim.master_kpi_id, sysdate)
when matched then
update
set pkpi.lastupdatedbydataload = sysdate;
Please check the results of this code with your expected outcome. As I said, reverse-engineering business logic is hard, and matching on master_kpi_id as well as programid is not the same as just deleting on programid.
You do not change v_UDF after first fetch. Then loop compare it with same first value... compare and compare... compare and compare.

Resources