Can I perform update inside PL/SQL function? - oracle

is it possible, to update table within function? Because mine does not do anything..
CREATE OR REPLACE FUNCTION rep_ort(id INT, status VARCHAR2, end_date DATE, explanation VARCHAR2)
RETURN VARCHAR2
IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
UPDATE reports
SET
id = id,
status = status,
end_date = end_date,
explanation = explanation;
commit;
RETURN 'Updated';
END;
select rep_ort('5','Closed','2021-01-12 17:30','Client fault') from dual;

Yours is doing nothing because you named parameters with the same name as columns, so you're just updating the whole table to the same values.
Rename parameters and - maybe - include WHERE clause.
Although you can do it, procedures are meant to be used for such a purpose.
CREATE OR REPLACE FUNCTION rep_ort
(p_id INT, p_status VARCHAR2, p_end_date DATE, p_explanation VARCHAR2)
RETURN VARCHAR2
IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
UPDATE reports
SET
id = p_id,
status = p_status,
end_date = p_end_date,
explanation = p_explanation;
commit;
RETURN 'Updated';
END;

Related

stored procedure compiled with errors

I have a stored procedure where I am trying to take 2 input age and name
That will update the record in employee table.
But when I am compiling getting - compiled with errors
Code :
CREATE OR REPLACE PROCEDURE update_emp_age
(
v_age in number;
v_ename in varchar2(255 char);
) AS
BEGIN
UPDATE employee
SET
eage = v_age
WHERE
ename = v_ename;
ROLLBACK;
END;
Remove the size from the data types in the signature.
Use commas and not semi-colons between arguments in the signature.
Don't ROLLBACK (else your query will perform the update and then immediately ROLLBACK the change doing lots of work for no effect and potentially rolling back prior chages).
Like this:
CREATE OR REPLACE PROCEDURE update_emp_age
(
v_age in NUMBER,
v_ename in VARCHAR2
) AS
BEGIN
UPDATE employee
SET eage = v_age
WHERE ename = v_ename;
END;
/
You could also use %TYPE in the signature:
CREATE OR REPLACE PROCEDURE update_emp_age
(
v_age in EMPLOYEE.EAGE%TYPE,
v_ename in EMPLOYEE.ENAME%TYPE
) AS
BEGIN
UPDATE employee
SET eage = v_age
WHERE ename = v_ename;
END;
/
db<>fiddle here

How can I run this stored procedure with options?

CREATE TABLE Docente
(
opcion varchar2(10),
id_Docente NUMBER,
nombre VARCHAR2(30),
apellido VARCHAR2(30),
cedula NUMBER,
titulo VARCHAR2(100),
observaciones VARCHAR2(200),
estado VARCHAR2(10),
mensaje varchar2(50) NULL,
CONSTRAINT PK_Docente PRIMARY KEY (id_Docente)
);
CREATE OR REPLACE PROCEDURE sp_crud_docente(
p_opcion varchar2,
p_id_Docente NUMBER,
p_nombre VARCHAR2,
p_apellido VARCHAR2,
p_cedula NUMBER,
p_titulo VARCHAR2,
p_observaciones VARCHAR2,
p_estado VARCHAR2,
p_mensaje out varchar2)
as
v_valor int;
v_row Docente%rowtype;
begin
if (p_opcion = 'I') then
begin
Select MAX(id_Docente)+ 1 into v_valor from Docente;
if v_valor is null then
v_valor := 1;
p_mensaje:= 'Registro inserted...';
DBMS_OUTPUT.PUT_LINE(p_mensaje);
insert into Docente
values (p_opcion,v_valor,p_id_Docente,p_nombre,p_apellido,p_cedula,p_titulo,p_observaciones,p_estado);
end if;
end;
else if (p_opcion = 'U') then
update Docente set opcion=p_opcion,
id_Docente=p_id_Docente,
nombre=p_nombre,
apellido=p_apellido,
cedula=p_cedula,
titulo=p_titulo,
observaciones=p_observaciones,
estado=p_estado
where id_Docente=p_id_Docente;
p_mensaje:= 'Registro updated...';
DBMS_OUTPUT.PUT_LINE(p_mensaje);
else if (p_opcion = 'S') then
begin
Select
opcion,
id_Docente,
nombre,
apellido,
cedula,
titulo,
observaciones,
estado,
mensaje
into v_row
from Docente;
p_mensaje := ('Ok');
DBMS_OUTPUT.PUT_LINE(p_mensaje||' --> '||v_row.opcion||'|'||v_row.id_Docente||'|'||v_row.nombre||'|'||v_row.apellido||'|'||v_row.cedula||'|'||v_row.titulo||'|'||v_row.observaciones||'|'||v_row.estado||'|'||v_row.mensaje);
end;
else if (p_opcion = 'D') then
Delete from Docente where id_Docente=p_id_Docente;
p_mensaje := ('Proceso ejecutado correctamente');
DBMS_OUTPUT.PUT_LINE(p_mensaje);
commit;
end if;
end if;
end if;
end if;
EXCEPTION
WHEN OTHERS then
p_mensaje := ('ERROR. No se pudo ejecutar el proceso');
rollback;
end;
Take out the length of the incoming parameters:
create or replace procedure sp_crud_docente (
p_opcion varchar2,
p_id_Docente NUMBER,
p_nombre VARCHAR2,
p_apellido VARCHAR2,
p_cedula NUMBER,
p_titulo VARCHAR2,
p_observaciones VARCHAR2,
p_estado VARCHAR2,
p_mensaje out varchar2)
See 8.7.1 Formal and Actual Subprogram Parameters section of the 19c manual "Database PL/SQL Language Reference"
Bobby
Apart from the fact that varchar2 parameters can't have size, this is practically unsolvable without you posting the docente table description.
Bad habits kick; you're wrongly relying on the fact that you know what you're doing, but it turns out you don't.
I can't figure out how many nor which columns docente contains, and in which order.
insert into docente should explicitly mention all columns you're inserting into, one-by-one. Code you wrote suggests that table contains 9 columns (opcion, id_docente, nombre, apellido, cedula, titulo, observaciones, estado).
update, on the other hand, updates only 6 of them (nombre, apellido, cedula, tutlo, observaciones, estado) which doesn't collide with insert; it is OK if you don't update all columns.
However, p_opcion = 'S' selects only 7 columns into v_row which is declared as docente%rowtype. Out of those 7 columns, I believe you "forgot" a comma between id_docente and nombre - those are two columns, it's not that nombre is id_docente's alias, right? Suppose it is 8 columns that are selected after all. Now, that collides with number of columns you used in insert (9 of them). If you're selecting into %rowtype, you must select ALL columns in EXACT ORDER.
I suggest you review code you wrote, pay attention about what I said and fix those errors.

Oracle issue joining pipelined data

am trying to set up a test CASE, which calls a pipelined function that returns dates based on a DATE range. My goal is to have a row for each date.
I am getting the following error (see below) when I am trying to create the procedure. I know I have to JOIN the passed in p_id with the results returned back from the function but I can't seem to figure out how since there is no link to JOIN them with.
Can someone provide the correct code to fix this issue and explain what I did wrong. Thanks in advance to all who answer and your expertise.
Errors: PROCEDURE CREATE_DATA
Line/Col: 8/10 PL/SQL: SQL Statement ignored
Line/Col: 11/13 PL/SQL: ORA-00936: missing expression
CREATE OR REPLACE TYPE nt_date IS TABLE OF DATE;
CREATE OR REPLACE FUNCTION generate_dates_pipelined(
p_from IN DATE,
p_to IN DATE
)
RETURN nt_date PIPELINED DETERMINISTIC
IS
v_start DATE := TRUNC(LEAST(p_from, p_to));
v_end DATE := TRUNC(GREATEST(p_from, p_to));
BEGIN
LOOP
PIPE ROW (v_start);
EXIT WHEN v_start >= v_end;
v_start := v_start + INTERVAL '1' DAY;
END LOOP;
RETURN;
END generate_dates_pipelined;
CREATE TABLE data(
d_id NUMBER(6),
d_date DATE
);
CREATE OR REPLACE PROCEDURE create_data (
p_id IN NUMBER,
p_start_date IN DATE,
p_end_date IN DATE
)
IS
BEGIN
INSERT INTO data (d_id, d_date)
VALUES
(p_id,
TABLE( generate_dates_pipelined(p_start_date, p_end_date)
) c
);
END;
EXEC create_data (1, DATE '2021-08-21', DATE '2021-08-30');
Your table expects a single DATE per row; you are trying to provide a collection of dates per row.
You need to INSERT for each row of the collection and can do that with a SELECT statement:
CREATE OR REPLACE PROCEDURE create_data (
p_id IN NUMBER,
p_start_date IN DATE,
p_end_date IN DATE
)
IS
BEGIN
INSERT INTO data (d_id, d_date)
SELECT p_id,
COLUMN_VALUE
FROM TABLE(generate_dates_pipelined(p_start_date, p_end_date));
END;
/

Executing function embedded SELECT sql query to increase performance

I have the following SQL query that will return the name of a column in a specific table. Let's say it return 'USER_PK' as column name when it runs.
the query:
SELECT max(COLUMN_NAME)
FROM ALL_TAB_COLUMNS
WHERE OWNER= 'DW_01'
AND table_name='D_O_USERS'
AND COLUMN_NAME<>'USER_PK';
Now I would like to run the above query as part of a function but instead of running it and storing the value it returns in a variable (using INTO or attribution like initial_sql: = '...', followed by exec ) I would need to have it run inside one line of code as below (see part in bold)... So far I have been unsuccessful as it is interpreted as a string when using quotes ...
CREATE OR REPLACE function DW_01.EXECUTE_AUTO (db_schema IN VARCHAR2, db_table IN VARCHAR2, pk_name IN VARCHAR2, id_pk IN INTEGER) RETURN VARCHAR2
IS
result VARCHAR2(4000);
begin
EXECUTE IMMEDIATE 'select STANDARD_HASH( '|| **SELECT max( COLUMN_NAME) FROM ALL_TAB_COLUMNS WHERE OWNER='' || db_schema || '' AND table_name=''||db_table ||'' AND COLUMN_NAME<>'' ||pk_name ||'** ,''SHA512'' ) from '||db_table||' where '|| pk_name ||'='||id_pk into RESULT ;
return result;
end;
Many thanks in advance for your thoughts!
You need to amend you r code like below -
CREATE OR REPLACE function DW_01.EXECUTE_AUTO (db_schema IN VARCHAR2,
db_table IN VARCHAR2,
pk_name IN VARCHAR2,
id_pk IN INTEGER) RETURN VARCHAR2
IS
result VARCHAR2(4000);
begin
EXECUTE IMMEDIATE 'select STANDARD_HASH( ' || pk_name || ',256 )
from '||db_table||' where '|| pk_name ||'='||id_pk into RESULT;
return result;
end;
/
There are only a few ways to run dynamic SQL in SQL, and they're neither pretty nor fast. The function below uses DBMS_XMLGEN.GETXML to dynamically run a SQL statement.
create or replace function execute_auto(db_schema in varchar2, db_table in varchar2, pk_name in varchar2, id_pk in integer) return varchar2
is
v_column_name varchar2(128);
v_result varchar2(4000);
begin
select standard_hash(to_number(extractValue(xml_results, '/ROWSET/ROW/' || max_column)), 'SHA512') hash_value
into v_result
from
(
--Create a single XML file with the ROWIDs that match the condition.
select max(column_name) max_column, xmltype(dbms_xmlgen.getxml
('
select '||max(column_name)||'
from '||db_schema||'.'||db_table||'
where id = '||id_pk
)) xml_results
from all_tab_columns
where owner = db_schema
and table_name = db_table
and column_name <> pk_name
);
return v_result;
end;
/
For example, let's create this sample table with 100,000 rows:
--drop table test1;
create table test1(id number, a number, b number, constraint pk_test1 primary key(id));
insert into test1
select level, level, level from dual connect by level <= 100000;
commit;
This shows how to use the function;
select execute_auto(user, 'TEST1', 'ID', id) hash
from test1 where id = 1;
HASH
----
A36753F534728ED84A463ECB13750B8E920A7E4D90244258DE77D9800A0F3DAF8CBAD49602E960A2355933C689A23C30377CE10FC4B8E1F197739FF86C791022
In addition to problems with type conversion and SQL injection, the performance is terrible. Selecting all 100,000 rows this way takes 200 seconds on my machine.
select sum(length(execute_auto(user, 'TEST1', 'ID', id))) from test1;
Normally, running everything in a single SELECT statement is a good way to improve performance. But this extreme type of dynamic SQL will never run fast. You probably want to rethink your approach. Instead of trying to optimize the SQL inside a function that is run one-row-at-a-time, try to change the process to process once-per-table

Trying to assign a value in a variable and used it in proc in Oracle

i am trying to assign a value in a variable "slno" use it to insert value in table "Par_conn" . but it is giving error while executing the Procedure with parameter.Any help would be appreciated.
Table
create table PAR_CONN
(
CUSTOMER_ID NUMBER(10) not null,
CUSTOMER_NAME VARCHAR2(50) not null,
CITY VARCHAR2(50)
)
Store Procedure
CREATE OR REPLACE PROCEDURE sp_insert(CUSTOMER_NAME IN VARCHAR2,CITY IN VARCHAR2)
IS
Declare slno number;
BEGIN
select count(*) into slno from Par_conn;
insert into Par_conn values(slno,CUSTOMER_NAME,CITY);
commit;
END;
Executing SP:
Begin
sp_insert('ramesh','dispur');
end;
The syntax is wrong. See below comments inline. Not sure what you wanted to achieve but at the first place you need to make all syntatic corrections before implementing any logic.
CREATE OR REPLACE PROCEDURE sp_insert (CUSTOMER_NAME IN VARCHAR2,
CITY IN VARCHAR2)
IS
---Declare slno number; --<this is wrong way of declaration
slno NUMBER;
BEGIN
SELECT COUNT (*) INTO slno FROM Par_conn;
INSERT INTO Par_conn
VALUES (slno, CUSTOMER_NAME, CITY);
COMMIT;
END;
Refer here how to create a procedure .
Procedure
Please do not reinvent wheel.
You probably aimed for:
CREATE OR REPLACE PROCEDURE sp_insert(CUSTOMER_NAME IN VARCHAR2,CITY IN VARCHAR2)
IS
slno number;
BEGIN
select max(id_col_name)+1 into slno from Par_conn;
insert into Par_conn values(slno,CUSTOMER_NAME,CITY);
commit;
END;
to set id value. I would strongly recommend to use SEQUENCE or IDENTITY.
Also you should always specify column list:
insert into Par_conn(col1_name, col2_name, col3_name)
values(slno,CUSTOMER_NAME,CITY);

Resources