I have an error with trigger using if conditional and update - oracle

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.

Related

How to retrieve data from 3 tables using sub query oracle SQL

I want to retrieve users name and there responsibility_key where there end_date is null and i want to convert it to (sysdate+1) using nvl but i am only able to retrieve the responsibility_key not the name please help.
The error in the image says "column ambiguously defined". Take a close look. Your last END_DATE could refer to either the u alias or the table from the subquery. Change it to match the rest of your subquery (FIND_USER_GROUPS_DIRECT.END_DATE)
EDIT
Your query is
select u.USER_NAME, d.responsibility_key from FND_USER u,FND_RESPONSIBILITY_VL d
where responsibility_id in(
select responsibility_id from
FND_USER_RESP_GROUPS_DIRECT WHERE END_USER_RESP_GROUPS_DIRECT.END_DATE=nvl(END_DATE,sysdate+1)) and
u.END_DATE=nvl(END_DATE,SYSDATE + 1)
;
The query isn't formatted, which makes it hard to read.
Not all columns are qualified with table name (or aliases), as mentioned in the comments.
The query currently uses an implicit join.
The query is impossible to understand without seeing the table definitions (desc [table_name]).
For points 1 and 2, a properly formatted query will look something like
select u.user_name, d.responsibility_key
from
fnd_user u,
fnd_responsibility_vl d
where
d.responsibility_id in (
select urgd.responsibility_id
from
fnd_user_resp_groups_direct urgd
where
urgd.end_date = nvl(u.end_date, sysdate+1)
) and
u.end_date = nvl(urgd.end_date, sysdate + 1)
;
This makes it easier to read and in addition to this, you can see that without table definitions I guessed (see point 4) as to which tables the end_date column belongs in your query. If I had to guess, so does Oracle. That means you have an ambiguity problem. To fix it, take a close look at the end_date column as it appears in your original query and where you do not prefix it with anything, you need to prefix it with the appropriate alias (after you have aliased all your tables).
For point 3, you can write your query more clearly with an explicit join and by using aliases for all columns. As for the explicit join I have no idea what your tables look like but one possibility is something like
select u.user_name, d.responsibility_key
from fnd_user u
join fnd_responsibility_vl d
on u.id = d.user_id
where
d.responsibility_id in (
select responsibility_id
from fnd_user_resp_groups_direct urgd
where
urgd.end_date = nvl(u.end_date, sysdate+1)
) and
u.end_date = nvl(urgd.end_date, sysdate+1)
;
If you follow these points you will get to the root of the error.

Update statement with joins in Oracle

I need to update one column in table A with the result of a multiplication of one field from table A with one field from table B.
It would be pretty simple to do this in T-SQL, but I can't write the correct syntax in Oracle.
What I've tried:
UPDATE TABLE_A
SET TABLE_A.COLUMN_TO_UPDATE =
(select TABLE_A.COLUMN_WITH_SOME_VALUE * TABLE_B.COLUMN_WITH_PERCENTAGE
from TABLE_A
INNER JOIN TABLE_B
ON TABLE_A.PRODUCT_ID = TABLE_B.PRODUCT_ID
AND TABLE_A.SALES_CHANNEL_ID = TABLE_B.SALES_CHANNEL_ID)
WHERE TABLE_A.MONTH_ID IN (201601, 201602, 201603);
But I keep getting errors. Could anybody help me, please?
I generally prefer to use the below format for such cases since this will ensure there's no update performed if there's no data in the table(query extracted temp table) whereas in the above solution provided by Brian Leach will update the new value as null if there's no record present in the 2nd table but exists in the first table.
UPDATE
(
select TABLE_A.COLUMN_TO_UPDATE
, TABLE_A.PRODUCT_ID
, TABLE_A.COLUMN_WITH_SOME_VALUE * TABLE_B.COLUMN_WITH_PERCENTAGE as value
from TABLE_A
INNER JOIN TABLE_B
ON TABLE_A.PRODUCT_ID = TABLE_B.PRODUCT_ID
AND TABLE_A.SALES_CHANNEL_ID = TABLE_B.SALES_CHANNEL_ID
AND TABLE_A.MONTH_ID IN (201601, 201602, 201603)
) DATA
SET DATA.COLUMN_TO_UPDATE = DATA.value;
This solution can cause key preserved value issues which shouldn't be an issue here since i expect a single row in both the tables for one product(ID).
More on Key Preserved table concept in inner join can be found here
https://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:548422757486
#Jayesh Mulwani raiesed a valid point, this will set the value to null if there is no matching record. This may or may not be the desired result. If it isn't, and no change is desirect, you can change the select statement to:
coalesce((SELECT table_b.column_with_percentage
FROM table_b
WHERE table_a.product_id = table_b.product_id AND table_a.sales_channel_id = table_b.sales_channel_id),1)
If this is the desired outcome, Jayesh's solution will be more efficient as it will only update matching records.
UPDATE table_a
SET table_a.column_to_update = table_a.column_with_some_value
* (SELECT table_b.column_with_percentage
FROM table_b
WHERE table_a.product_id = table_b.product_id
AND table_a.sales_channel_id = table_b.sales_channel_id)
WHERE table_a.month_id IN (201601, 201602, 201603);

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.

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.

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