ORACLE PLSQL Function/Trigger - sql

I'm trying to create a FUNCTION/TRIGGER that can update the status_comunal_waste in table supply without the need of writing a query.
EDIT:
TRAFFIC (date),(input_quant),(output_quant),(status)
SUPPLY (input_quant), (output_quant),(status_comunal_waste)
After I import the DATE, INPUT, OUTPUT, STATUS data into the table TRAFFIC,
with the trigger AIR the data is also imported into the table SUPPLY, with the execption of columns USER_CHANGES, DATE_CHANGES, USER_DELETE, DATE_DELETE as to mimic the changes the "user" has made if the "user" decides to change or add the data.
So far I've tried something like this
create or replace TRIGGER supply_after_insert
after INSERT
ON traffic
FOR EACH ROW
DECLARE
Status_comunal_waste VARCHAR2(20);
BEGIN
SELECT SUM(input_quant) - SUM(output_quant)
into Status_comunal_waste
FROM traffic
WHERE (key_num_code =200101);
INSERT INTO supply
(Status_comunal_waste) VALUES (Status_comunal_waste);
END;
EDIT:
Meanwhile I switched from creating a trigger to function, and this is written so far:
create or replace
FUNCTION stat_comunal_waste (key_num_code number)
return number
is
status_comunal_waste NUMBER;
BEGIN
if (key_num_code = 200101) THEN
SELECT SUM (input_quant) - SUM (output_quant)
into status_comunal_waste
FROM traffic
WHERE key_num_code =200101;
end if;
return status_comunal_waste;
END;
The function compiled with no errors but now I get errors with the PACKAGE
CREATE OR REPLACE PACKAGE Paket
IS
function stat_comunal_waste (key_num_code number)
return status_comunal_waste;
end Paket;
/
Error(3,1): PL/SQL: Declaration ignored
Error(4,8): PLS-00488: 'status_comunal_waste' must be a type

This query might be wrong.
SELECT SUM(input_quant) - SUM(output_quant) into Status_comunal_waste
FROM traffic WHERE key_num_code =200101);
Make sure you are getting the correct values. Try to replace the above query with code below and give it a try
select SUM(input_quant) into input_q FROM traffic WHERE key_num_code =200101;
select SUM(output_quant) into output_q FROM traffic WHERE key_num_code =200101;
diff := input_q - output_q;
Final insert will be like:
INSERT INTO status (Status_comunal_waste) VALUES (diff);

Related

query has no destination for result data in a function that has a set of instructions in postgresql

I am trying to automate a set of sentences that I execute several times a day. For this I want to put them in a postgres function and just call the function to execute the sentences consecutively. If everything runs OK then in the end return the SUCCESS value. The following function replicates my idea and the error I am getting when executing the function:
CREATE OR REPLACE FUNCTION createTable() RETURNS int AS $$
BEGIN
DROP TABLE IF EXISTS MY_TABLE;
CREATE TABLE MY_TABLE
(
ID integer
)
WITH (
OIDS=FALSE
);
insert into MY_TABLE values(1);
select * from MY_TABLE;
RETURN 'SUCCESS';
END;
$$ LANGUAGE plpgsql;
Invocation:
select * from createTable();
With my ignorance of postgresql I would expect to obtain the SUCCESS value as a return (If everything runs without errors). But the returned message causes me confusion, isn't it the same as a function in any other programming language? When executing the function I get the following message:
query has no destination for result data Hint: If you want to
discard the results of a SELECT, use PERFORM instead.
query has no destination for result data Hint: If you want to discard the results of a SELECT, use PERFORM instead.
You are getting this error because you do not assign the results to any variable in the function. In a function, you would typically do something like this instead:
select * into var1 from MY_TABLE;
Therefore, your function would look something like this:
CREATE OR REPLACE FUNCTION createTable() RETURNS int AS $$
DECLARE
var1 my_table%ROWTYPE;
BEGIN
DROP TABLE IF EXISTS MY_TABLE;
CREATE TABLE MY_TABLE
(
ID integer
)
WITH (
OIDS=FALSE
);
insert into MY_TABLE values(1);
select * into var1 from MY_TABLE;
<do something with var1>
RETURN 'SUCCESS';
END;
$$ LANGUAGE plpgsql;
Otherwise, if you don't put the results into a variable, then you're likely hoping to achieve some side effect (like advancing a sequence or firing a trigger somehow). In that case, plpgsql expects you to use PERFORM instead of SELECT
Also, BTW your function RETURNS int but at the bottom of your definition you RETURN 'SUCCESS'. SUCCESS is a text type, not an int, so you will eventually get this error once you get past that first error message -- be sure to change it as necessary.

PostgreSQL variable in select and delete statements

The Problem: I have many delete lines in a PostgreSQL script where I am deleting data related to the same item in the database. Example:
delete from <table> where <column>=180;
delete from <anothertable> where <column>=180;
...
delete from <table> where <column>=180;
commit work;
There are about 15 delete statements deleting data that references <column>=180.
I have tried to replace the 180 with a variable so that I only have to change the variable, instead of all the lines in the code (like any good programmer would do). I can't seem to figure out how to do it, and it's not working.
NOTE: I am very much a SQL novice (I rarely use it), so I know there's probably a better way to do this, but please enlighten me on how I can fix this problem.
I have used these answers to try and fix it with no luck: first second third. I've even gone to the official PostgreSQL documentation, with no luck.
This is what I'm trying (these lines are just for testing and not in the actual script):
DO $$
DECLARE
variable INTEGER:
BEGIN
variable := 101;
SELECT * FROM <table> WHERE <column> = variable;
END $$;
I've also tried just delcaring it like this:
DECLARE variable INTEGER := 101;
Whenever I run the script after replacing one of the numbers with a variable this is the error I get:
SQL Error [42601]: ERROR: query has no destination for result data
Hint: If you want to discard the results of a SELECT, use PERFORM instead.
Where: PL/pgSQL function inline_code_block line 6 at SQL statement
Can someone tell me where I'm going wrong? It would be nice to only have to change the number in the variable, instead of in all the lines in the script, and I just can't seem to figure it out.
As #Vao Tsun said, you must define a destination to your SELECT statement. Use PERFORM otherwise:
--Test data
CREATE TEMP TABLE my_table (id, description) AS
VALUES (1, 'test 1'), (2, 'test 2'), (101, 'test 101');
--Example procedure
CREATE OR REPLACE FUNCTION my_procedure(my_arg my_table) RETURNS VOID AS $$
BEGIN
RAISE INFO 'Procedure: %,%', my_arg.id, my_arg.description;
END
$$ LANGUAGE plpgsql;
DO $$
DECLARE
variable INTEGER;
my_record my_table%rowtype;
BEGIN
variable := 101;
--Use your SELECT inside a LOOP to work with result
FOR my_record IN SELECT * FROM my_table WHERE id = variable LOOP
RAISE INFO 'Loop: %,%', my_record.id, my_record.description;
END LOOP;
--Use SELECT to populate a variable.
--In this case you MUST define a destination to your result data
SELECT * INTO STRICT my_record FROM my_table WHERE id = variable;
RAISE INFO 'Select: %,%', my_record.id, my_record.description;
--Use PERFORM instead of SELECT if you want to discard result data
--It's often used to call a procedure
PERFORM my_procedure(t) FROM my_table AS t WHERE id = variable;
END $$;
--DROP FUNCTION my_procedure(my_table);

Oracle - Is there any way I can get around SELECT INTO on PL/SQL block

I am trying to create a trigger, which automatically updates a student's application state when the application status row in the application table changes. I have been browsing the web for a little over an hour or so now and despite finding a potential work around using EXECUTE IMMEDIATE I cannot achieve my desired result (EXECUTE IMMEDIATE was causing an unbound variable error).
Trigger code
CREATE OR REPLACE TRIGGER trg_applications
BEFORE INSERT OR UPDATE ON applications FOR EACH ROW
BEGIN
IF UPDATING THEN
/* If the status is ACCEPTED, then approve the students application */
SELECT CASE
WHEN get_status(:NEW.status_id) =
LOWER('Applicant Accepted Offer')
THEN student_accept_offer( :NEW.student_id )
END
FROM status;
END IF;
END;
The get status method returns a VARCHAR2 to check whether the new status matches the condition, if so I want to update the student_approved row using the autonomous_transaction below.
student_accept_offer code
CREATE OR REPLACE FUNCTION student_accept_offer( this_stu_id NUMBER )
RETURN VARCHAR2 IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
UPDATE students
SET students.student_on_placement = 1
WHERE students.student_id = this_stu_id;
COMMIT;
RETURN 'Student has approved application';
END student_accept_offer;
This function works as intended when I test it outside of my trigger, however when it is embedded in the trigger an PLS-00428 error gets thrown. Could anyone point me in the right direction as to how can I work around this, to allow me to have this function fire automatically on an update if the status matches.
Thanks for your time
EDIT - Tables I am referencing
Changing your code slightly to remove the SELECT statement (as it seems unnecessary) then does this work?
CREATE OR REPLACE TRIGGER trg_applications
BEFORE INSERT OR UPDATE ON applications FOR EACH ROW
BEGIN
IF UPDATING THEN
/* If the status is ACCEPTED, then approve the students application */
IF get_status(:NEW.status_id) = 'applicant accepted offer' THEN
student_accept_offer( :NEW.student_id );
END IF;
END IF;
END;

PL/pgSQL function + client-side lo_import

I have problem with importing documents to postgres db. I have plpgsql function, simplier version could look like that:
create function add_file(flag integer, sth varchar) returns void as
begin
if flag = 1 then
insert into tab_one values (my_file_oid, sth);
else
insert into tab_two values (my_file_oid, sth);
end if;
end;
And psql command:
\lo_import('path/to/file');
Both code in one file. I cant put lo_import() to insert statement, becouse I need client-site lo_import. There is variable LASTOID, but it is not avaible in add_file function. And it wouldnt be updating on every call add_file().
So, how can I put oid to database with, in our example, 'flag' and 'sth' by insert statement and everything in function with arguments? File is in client computer.
psql's \lo_import returns the OID resulting from the import. You need to hand that in as parameter to the function, which could look like this:
CREATE FUNCTION add_file(_flag integer, _sth varchar, _oid oid)
RETURNS void LANGUAGE plpgsql AS
BEGIN
IF _flag = 1 THEN
INSERT INTO tab_one(file_oid, sth) VALUES (_oid, _sth);
ELSE
INSERT INTO tab_two(file_oid, sth) VALUES (_oid, _sth);
END IF;
END;
As an aside: always add a column list to your table with an INSERT command (except for ad-hoc calls, maybe).
From within a plpgsql function you can make use to the also provided server side functions. Could look like this:
INSERT INTO tab_one(file_oid, sth) VALUES (lo_import('/etc/motd'), _sth);
Note that this operates within the file system of the database server with the privileges of the owner (usually system user postgres). Therefore, use is restricted to superusers.

Solution to "cannot perform a DML operation inside a query"?

I am using a Data Analysis tool and the requirement I have was to accept a value from the user, pass that as a parameter and store it in a table. Pretty straighforward so I sat to write this
create or replace
procedure complex(datainput in VARCHAR2)
is
begin
insert into dumtab values (datainput);
end complex;
I executed this in SQL Developer using the following statement
begin
complex('SomeValue');
end;
It worked fine, and the value was inserted into the table. However, the above statements are not supported in the Data Analysis tool, so I resorted to use a function instead. The following is the code of the function, it compiles.
create or replace
function supercomplex(datainput in VARCHAR2)
return varchar2
is
begin
insert into dumtab values (datainput);
return 'done';
end supercomplex;
Once again I tried executing it in SQL Developer, but I got cannot perform a DML operation inside a query upon executing the following code
select supercomplex('somevalue') from dual;
My question is
- I need a statement that can run the mentioned function in SQL Developer or
- A function that can perform what I am looking for which can be executed by the select statement.
- If it is not possible to do what I'm asking, I would like a reason so I can inform my manager as I am very new (like a week old?) to PL/SQL so I am not aware of the rules and syntaxes.
P.S. How I wish this was C++ or even Java :(
EDIT
I need to run the function on SQL Developer because before running it in DMine (which is the tool) in order to test if it is valid or not. Anything invalid in SQL is also invalid in DMine, but not the other way around.
Thanks for the help, I understood the situation and as to why it is illegal/not recommended
You could use the directive pragma autonomous_transaction. This will run the function into an independant transaction that will be able to perform DML without raising the ORA-14551.
Be aware that since the autonomous transaction is independent, the results of the DML will be commited outside of the scope of the parent transaction. In most cases that would not be an acceptable workaround.
SQL> CREATE OR REPLACE FUNCTION supercomplex(datainput IN VARCHAR2)
2 RETURN VARCHAR2 IS
3 PRAGMA AUTONOMOUS_TRANSACTION;
4 BEGIN
5 INSERT INTO dumtab VALUES (datainput);
6 COMMIT;
7 RETURN 'done';
8 END supercomplex;
9 /
Function created
SQL> SELECT supercomplex('somevalue') FROM dual;
SUPERCOMPLEX('SOMEVALUE')
--------------------------------------------------------------------------------
done
SQL> select * from dumtab;
A
--------------------------------------------------------------------------------
somevalue
Tom Kyte has a nice explanation about why the error is raised in the first place. It is not safe because it may depend upon the order in which the rows are processed. Furthermore, Oracle doesn't guarantee that the function will be executed at least once and at most once per row.
Just declare a variable to accept the return value, for example:
declare
retvar varchar2(4);
begin
retvar := supercomplex('somevalue');
end;
The select doesn't work because the function is performing an insert, if all it did was return a value then it would work.
Just execute the function in a dummy if ... end if; statement to ignore the return value:
exec if supercomplex('somevalue') then null; end if;
Or execute it as a parameter for put_line procedure to output the return value:
exec dbms_ouput ('result of supercomplex='||supercomplex('somevalue'));
result of supercomplex=done