Replace SELECT INTO statement with a cursor ORACLE - oracle

This is the query. How to replace SELECT INTO statement with a cursor?
I'm newbie in Oracle
Thanks for your help
SELECT CEQ_LISTE_TYPE_QUESTIONS.ID_LISTE_TYPE_QUESTION
INTO vintIdListeTypeQuestion
FROM CEQ_FORMULAIRES
inner join CEQ_LISTE_TYPE_QUESTIONS
on CEQ_LISTE_TYPE_QUESTIONS.ID_LISTE_TYPE_FORMULAIRE=CEQ_FORMULAIRES.ID_TYPE_FORMULAIRE
AND CEQ_LISTE_TYPE_QUESTIONS.WEBCODE='ITEM_BETA_LACTAMASE'
WHERE CEQ_FORMULAIRES.ID_FORMULAIRE=to_number(out_rec.ID_FORMULAIRE)
and ceq_formulaires.date_inactive is null;

The error tells you that the query returns more than 1 row, so you should determine which row you need. Here an example how to fetch the most recent row based on a date field I thought up in ceq_list_type_questions "some_date".
select max(q.id_liste_type_question) keep (dense_rank last order by q.some_date) into vintidlistetypequestion
from ceq_formulaires f
join ceq_liste_type_questions q on q.id_liste_type_formulaire = f.id_type_formulaire
where f.id_formulaire = to_number(out_rec.id_formulaire)
and f.date_inactive is null
and q.webcode = 'ITEM_BETA_LACTAMASE'

Well, if you want to process your multiple rows in a loop, it's as simple as
BEGIN
FOR curs IN (SELECT ceq_liste_type_questions.id_liste_type_question
FROM ceq_formulaires
INNER JOIN ceq_liste_type_questions ON ceq_liste_type_questions.id_liste_type_formulaire=ceq_formulaires.id_type_formulaire
AND ceq_liste_type_questions.webcode = 'ITEM_BETA_LACTAMASE'
WHERE ceq_formulaires.id_formulaire = TO_NUMBER(out_rec.id_formulaire)
AND ceq_formulaires.date_inactive IS NULL)
LOOP
DBMS_OUTPUT.PUT_LINE(curs.id_liste_type_question); -- do what you need to do
END LOOP;
END;
/
But, as BazzPsychoNut mentions, if it's a requirement that your SQL return/operate on a single row, you'll need to modify your query to meet that requirement.

Related

I have an error with trigger using if conditional and update

Executing this trigger in ORACLE I got the following error:
Table, View Or Sequence reference 'OCEX_COMI.FECHA_ASIG_GT' not allowed in this context.
This is my trigger:
create or replace trigger ocex_comi_total
before insert or update of id_gt,fecha_asig_gt on ocex_comi
for each row
begin
if ((ocex_comi.fecha_asig_gt > to_date('2018-12-15','yyyy-mm-dd')) and
(ocex_comi.fecha_asig_gt < to_date('2019-01-01','yyyy-mm-dd')))
then
update ocex_comi cm set
cm.PAGO_COM = (select uea.total_bono_especial from OCEX_UEA uea
join OCEX_GUIA_TRANSITO gt on uea.id_uea = gt.dest_id
where gt.cod_gt=cm.id_gt)
where cm.id_gt = (select gt.cod_gt from ocex_guia_transito gt JOIN
ocex_uea uea on uea.id_uea=gt.dest_id
where gt.cod_gt=cm.id_gt);
else
update ocex_comi cm set
cm.PAGO_COM = (select uea.total_x_pnp from OCEX_UEA uea
join OCEX_GUIA_TRANSITO gt on uea.id_uea = gt.dest_id
where gt.cod_gt=cm.id_gt)
where cm.id_gt = (select gt.cod_gt from ocex_guia_transito gt JOIN
ocex_uea uea on uea.id_uea=gt.dest_id
where gt.cod_gt=cm.id_gt);
end if;
end;
Well what I was trying to do is that with this trigger the column "PAGO_COM" of the table "ocex_comi" is automatically filled in from the table "ocex_uea" thanks to the column "total_bono_special" (if in case the date of the field "date_asig_gt" is included) between 15-dec to 31-dec) and if not, fill in the column "total_x_pnp" (if the date of the field "fecha_asig_gt is not between 15-dec to 31-dec.) Some idea or help with the error that comes to me, thanks.
Don't refer to the table column directly; you need to refer to the new pseudorecord:
if ((:new.fecha_asig_gt > to_date('2018-12-15','yyyy-mm-dd')) and
(:new.fecha_asig_gt < to_date('2019-01-01','yyyy-mm-dd')))
then
though I'd use date literals:
if :new.fecha_asig_gt > date '2018-12-15' and
:new.fecha_asig_gt < date '2019-01-01'
then
(Not sure if you really want >= rather than >, though.)
But you are also trying to update all rows in the table inside each branch of your logic, which doesn't sound like something you really want to do anyway, and which will cause a mutating table error at runtime if you try.
It's not really clear quite what your intent is there but I think you want something more like:
...
then
select uea.total_bono_especial
into :new.PAGO_COM
from OCEX_UEA uea
join OCEX_GUIA_TRANSITO gt on uea.id_uea = gt.dest_id
where gt.cod_gt = :new.id_gt;
else
...
and the same kind of thing in the other branch.
As those queries are so similar you could replace the if/else logic with a single query, whoch uses a case expression to decide which of the two column values to return.

Iterate through cursor and storing the output of the cursor in another table

I am trying to iterate through a cursor which stores the value of the table. I use a FOR Loop to iterate and IF one of the conditions is met, I store the output in another table. I am not sure of the approach I am following and also getting error(ORA-00933: SQL command not ended properly). Stats_Queries is my reference table where I iterate my cursor through. STATS_RESULT_CARD is my output table where I have to store the results. Please help.
DECLARE
CURSOR c1 IS
select Stats_Queries.OBJECTTYPE, Stats_Queries.CATEGORY, Stats_Queries.QUERY
from Stats_Queries;
r1 c1%ROWTYPE;
BEGIN
FOR r1 IN c1 LOOP
If (r1.OBJECTTYPE = 'CARD') THEN
INSERT INTO STATS_RESULTS_CARD (NODETYPENAME, NODEDEFNAME , CARDTYPENAME, PROVISIONSTATUSNAME, STATDATE, CARDCOUNT)
select nt.name, nd.name, ct.name, ps.name, sysdate, count(c.cardid)
from cardtype ct, card c, node n, nodetype nt, nodedef nd, provisionstatus ps
where ct.name in ('SRA AMP', 'XLA AMP', 'SAM', 'ESAM')
and ct.cardtypeid = c.card2cardtype
and c.card2node = n.nodeid
and n.node2nodetype = nt.nodetypeid
and n.node2nodedef = nd.nodedefid
and c.card2provisionstatus = ps.provisionstatusid
group by nt.name, nd.name, ct.name, ps.name
END If;
END LOOP;
END;
As an aside from the answer that Finbarr has provided (which is perfectly correct; add in the missing semi-colon and your procedure should work), why do you need to loop through the cursor at all? That's the slow way of doing it.
You could just do a single insert statement instead, such as:
insert into stats_results_card (nodetypename,
nodedefname,
cardtypename,
provisionstatusname,
statdate,
cardcount)
select x.nt_name,
x.nd_name,
x.ct_name,
x.ps_name,
x.statdate,
x.cnt_cardid
from (select nt.name nt_name,
nd.name nd_name,
ct.name ct_name,
ps.name ps_name,
sysdate statdate,
count (c.cardid) cnt_cardid
from cardtype ct,
card c,
node n,
nodetype nt,
nodedef nd,
provisionstatus ps
where ct.name in ('SRA AMP',
'XLA AMP',
'SAM',
'ESAM')
and ct.cardtypeid = c.card2cardtype
and c.card2node = n.nodeid
and n.node2nodetype = nt.nodetypeid
and n.node2nodedef = nd.nodedefid
and c.card2provisionstatus = ps.provisionstatusid
group by nt.name,
nd.name,
ct.name,
ps.name) x
cross join (select stats_queries.objecttype,
stats_queries.category,
stats_queries.query
from stats_queries
where objecttype = 'CARD');
N.B. This assumes that there really isn't any link between the original cursor and the select statement that was inside the loop; we do a cross join to replicate the rows the required number of times.
If there was an actual join between the two queries, you would put that in place of the cross join.
ORA-00933: SQL command not ended properly
Probably occurring because you missed a semicolon after
group by nt.name, nd.name, ct.name, ps.name

Comparing two tables, if rows are different, run query in Oracle

Think of my two tables have the same columns. One column is the ID, and the other one is the text. Is it possible to implement the following pseudo code in PLSQL?
Compare each row (They will have the same ID)
If anything is different about them
Run a couple of queries: an Update, and an Insert
ElseIf they are the same
Do nothing
Else the row does not exist
So add the row to the table compared on
Is it easy to do this using PLSQL or should I create a standalone application to do do this logic.
As your table have the same columns, by using NATURAL JOIN you can easily check if two corresponding rows are identical -- without need to update your code if a column is added to your table.
In addition, using OUTER JOIN allow you to find the rows present in one table but not in the other.
So, you can use something like that to achieve your purpose:
for rec in (
SELECT T.ID ID1,
U.ID ID2,
V.EQ
FROM T
FULL OUTER JOIN U ON T.ID = U.ID
FULL OUTER JOIN (SELECT ID, 1 EQ FROM T NATURAL JOIN U) V ON U.ID = V.ID)
loop
if rec.id1 is null
then
-- row in U but not in T
elsif rec.id2 is null
then
-- row in T but not in U
elsif rec.eq is null
-- row present in both tables
-- but content mismatch
end if
end loop
Else the row does not exist
So add the row to the table compared on
Is this condition means that rows can be missed in both tables? If only in one, then:
insert into t1 (id, text)
select id, text
from t2
minus
select id, text
from t1;
If missed records can be in both tables, you need the same query that inserts into table t2 rows from t1.
If anything is different about them
If you need one action for any amount of different rows, then use something like this:
select count(*)
into a
from t1, t2
where t1.id = t2.id and t1.text <> t2.text;
if a > 0 then
...
otherwise:
for i in (
select *
from t1, t2
where t1.id = t2.id and t1.text <> t2.text) loop
<do something>
end loop;
A 'merge' statement is what u needed.
Here is the syntax:
MERGE INTO TARGET_TABLE
USING SOURCE_TABLE
ON (CONDITION)
WHEN MATCHED THEN
UPDATE SET (DO YOUR UPDATES)
WHEN NOT MATCHED THEN
(INSERT YOUR NEW ROWS)
Google MERGE syntax for more about the statement.
Just use MINUS.
query_1
MINUS
query_2
In your case, if you really want to use PL/SQL, then select count into a local variable. Write a logic, if count > 0 then do other stuff.

Oracle: MIN() Statement causes empty row returns

I'm having a small issue with sorting the data returned from a query, with the aim of getting the oldest updated value in dataset so that I can update only that record. Here's what I'm doing:
WHERE ROWNUM = 1 AND TABLE1.ID != V_IGNOREID
AND TABLE1.LASTREADTIME = (SELECT MIN(TABLE1.LASTREADTIME) FROM TABLE1)
ORDER BY TABLE1.LASTREADTIME DESC;
It makes no difference as to whether the ORDER BY statement is included or not. If I only use the ROWNUM and equality checks, I get data, but it alternates between only two rows, which is why I'm trying to use the LASTREADTIME data (so that I can modify more than these two rows). Anybody have any thoughts on this, or any suggestions as to how I can use the MIN function effectively?
Cheers
select * from (
-- your original select without rownum and with order by
)
WHERE ROWNUM = 1
EDIT some explanation
I think the order by clause is applied on the resultset after the where clause. So if the rownum = 1 is in the same select statement with the order by, then it will be applied first and the order by will order only 1 row, which will be the first row of the unordered resultset.

How to loop in sql?

I dont want to use the "loop" related keyword, how can I implement loop with basic sql command in oracle ?
I have two table :
A:
ID, Color
B,
ID, AID, Type
I want to loop all records in B, and if ID = AID, then set the A.Color = B.Type
Thanks in advance !
Looping is, by definition, a procedural construct.
SQL is declarative: tell the database what you want done, not how to do it.
If you're absolutely convinced that you need to program such a thing, then write it in PL/SQL, Oracle's procedural language.
Bu I'm sure that it's possible to do what you want in SQL using an UPDATE with a WHERE clause.
Something like this (corrected per NullUserException):
UPDATE A SET A.Color = (SELECT B.Type FROM B WHERE A.ID = B.AID)
An alternate method:
MERGE INTO a
USING b
ON (b.aid = a.id)
WHEN MATCHED THEN UPDATE SET a.color = b.type;
You could just do:
UPDATE tablea a
SET a.color = (SELECT b.type
FROM tableb b
WHERE b.aid = a.id)
See this SQL script.
To do that you will have to write a stored procedure using PL/SQL. Here is the oracle page with some info and papers on the topic.
As others pointed out, you can probably solve your problem with a normal DML statement, without any looping involved. But to give you some basics on how to accomplish what you asked for in PL/SQL, here's an example...
DECLARE
CURSOR c IS
SELECT id, aid, type FROM b;
statement VARCHAR2(200);
BEGIN
FOR iterator IN c LOOP
IF iterator.id = iterator.aid THEN
statement := 'UPDATE a SET color = ' || iterator.type || 'WHERE id = ' || iterator.id;
EXECUTE IMMEDIATE statement;
END IF;
END LOOP;
END;
This anonymous PL/SQL block will iterate through each record in table b, and if b.id = b.aid, it will update table a and set a.color = b.type where a.id = b.id.
This seems to be what you were asking for. It's not exactly an efficient way to go about doing things, since you're firing off one DML statement per row in table b that has b.id=b.aid. But I wanted more to give this as a syntax example. This is just one way to iterate through a cursor by the way; you can also explicitly open cursors and fetch records, but it's easier this way if you don't need to do anything but iterate over the entire result set.

Resources