How to loop in sql? - oracle

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.

Related

Pl/SQL query a view in function

I have the function below
CREATE OR REPLACE FUNCTION BUTCE_REPORT_Fun (birim_id IN VARCHAR2)
RETURN sys_refcursor
IS
retval sys_refcursor;
BEGIN
OPEN retval FOR
select *
from ifsapp.butce_gerceklesme
WHERE budget_year = '2018'
AND USER_GROUP = birim_id ;
RETURN retval;
END BUTCE_REPORT_Fun;
and am trying to execute the function this way
SELECT * from table(IFSAPP.BUTCE_REPORT_FUN('3008'))
the line above generates this exception
ora-22905 cannot access rows from a non-nested table item
to keep in mind that ifsapp.butce_gerceklesme is a view (which I do not think that it matters).
So how I can solve this. any help is appreciated.
Actually, am trying to create a function that returns rows from the view above according to the parameters provided. so if I can achieve that in another way that would be better.
Ref Cursors are for use in program calls: they map to JDBC or ODBC ResultSet classes. They can't be used as an input to a table() call. Besides, there is no value in calling your function in SQL because you can simply execute the embedded query in SQL.
the main table is huge and the inner query assigned to USER_GROUP is selected every time
So maybe what you want is subquery factoring AKA the WITH clause?
with ug as (
select con2.CODE_PART_VALUE
from IFSAPP.ACCOUNTING_ATTRIBUTE_CON2 con2
where COMPANY = 'XYZ'
and ATTRIBUTE = 'ABC'
and CODE_PART = 'J'
and con2.ATTRIBUTE_VALUE=407
AND rownum = 1
)
select *
from ifsapp.butce_gerceklesme t
join ug on t.USER_GROUP = ug.CODE_PART_VALUE
WHERE t.budget_year = '2018'
Tuning queries on StackOverflow is a mug's game, because there are so many things which might be responsible for sub-optimal performance. But as a rule of thumb you should try to tune the whole query. Encapsulating a part of it in PL/SQL is unlikely to improve response times, and indeed may degrade them.

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.

An INTO clause is expected in this SELECT statement

any help for this please ?
I want to return all those who live in france, then compare them with those who live in BELguim if they have the same name or name_done show the result
declare
cursor c is select namer from poeple where city = 'France';
c_res c%rowtype;
begin
open c;
loop
fetch c into c_res;
exit when c%notfound;
select * from poeple tw where city = 'BELguim' AND (c_res.namer = tw.namer OR c_res.namer = tw.namer || ' _done' );
end loop;
close c;
end;
Why use a cursor? You can write this with a single query:
select pb.*
from poeple pf join
poeple pb
on pf.city = 'France' and pb.city = 'BELgium' and
(pf.namer = pb.namer or pf.namer = pb.namer || ' _done');
The issue with your code is that PL/SQL does not allow queries to return anything from code blocks. You can print out the results, insert values into variables, or put them in another table. But, you cannot just have the results from the query.
The issue is with :
select * from poeple tw where city='BELguim' AND (c_res.namer
=tw.namer OR c_res.namer =tw.namer || ' _done' );
You cannot write the SQL directly in PL/SQL. The PL/SQL engine expects an INTO clause.
Looking at your code, you don't need the cursor c. it could be written in a single SQL. Which brings it back to Gordon's query :
select pb.*
from poeple pf join
poeple pb
on pf.city = 'France' and pb.city = 'BELgium' and
(pf.namer = pb.namer or pf.namer = pb.namer || ' _done');
If you really need PL/SQL for some stuff which you have not explained, for example return the result set as REF CURSOR, then have an OUT parameter as REF CURSOR. But, you need to explain the requirement.
If you can do something in SQL, then don't use PL/SQL.
EDIT If your final goal is to update, then use the above query as USING clause in the MERGE statement.

Replace SELECT INTO statement with a cursor 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.

LINQ to SQL Insert

i'm using LINQ To SQL to perform an insert via db.table.InsertOnSubmit(). I'm wondering if there is a way to reproduce the T-SQL version of the 'where not exists (select etc etc) begin insert into etc etc end' as one single query? Thanks, Martin
LINQ has an extension method called Contains which allows for this functionality. This can be seen in the following example:
NorthwindDataContext dc = new NorthwindDataContext();
dc.Log = Console.Out;
var query =
from c in dc.Customers
where !(from o in dc.Orders
select o.CustomerID)
.Contains(c.CustomerID)
select c;
foreach (var c in query) Console.WriteLine( c );
Note the negation on the where clause!
This example was from the website here.
Nothing build in as far as I know, we would have to go about finding the row manually using where and than do the Insert.
There is a possibility of race coditions in such queries. Have a look at this thread for detailed solution :
http://social.msdn.microsoft.com/Forums/en-US/linqtosql/thread/b1a0eb5b-d5d3-41af-829f-bbbac47b7383/

Resources