select query with PL/SQL - sql

I ran this PL/SQL script, I want to store this PL/SQL in a procedure so that I would give it one parameter and returns info about that parameter, for now I ran it separately outside the procedure but I get the following error, please your help :
error signaled in parallel query server
declare
a VARCHAR2(100 CHAR);
b VARCHAR2(30 CHAR);
c VARCHAR2(50 CHAR);
d VARCHAR2(30 CHAR);
e VARCHAR2(100);
f VARCHAR2(30 CHAR);
BEGIN
SELECT
col1 ,
col2,
col3,
col4,
col5,
col6,
INTO
a,
b,
c,
d,
e,
f
FROM
MY_TABLE
WHERE
col1= 123;
dbms_output.put_line(a||','||b||','||c||','||d||','||e||','||f);
END;

There is a misplaced comma (,) after col6. Remove it the code will work:
declare
a VARCHAR2(100 CHAR);
b VARCHAR2(30 CHAR);
c VARCHAR2(50 CHAR);
d VARCHAR2(30 CHAR);
e VARCHAR2(100);
f VARCHAR2(30 CHAR);
BEGIN
SELECT
col1 ,
col2,
col3,
col4,
col5,
col6,
INTO
a,
b,
c,
d,
e,
f
FROM
MY_TABLE
WHERE
col1= 123;
dbms_output.put_line(a||','||b||','||c||','||d||','||e||','||f);
END;
If you want to create a procedure then here goes the code:
declare
a VARCHAR2(100 CHAR);
b VARCHAR2(30 CHAR);
c VARCHAR2(50 CHAR);
d VARCHAR2(30 CHAR);
e VARCHAR2(100);
f VARCHAR2(30 CHAR);
BEGIN
SELECT
col1 ,
col2,
col3,
col4,
col5,
col6
INTO
a,
b,
c,
d,
e,
f
FROM
MY_TABLE
WHERE
col1= 123;
dbms_output.put_line(a||','||b||','||c||','||d||','||e||','||f);
END;

I really was stuck on this, but this is my final answer as the query returns more than one row, and it works just fine.
CREATE OR REPLACE PROCEDURE my_fun
(
PARAM1 IN VARCHAR2
) AS
ref_curs SYS_REFCURSOR;
a VARCHAR2(100 CHAR);
b VARCHAR2(30 CHAR);
c VARCHAR2(50 CHAR);
d VARCHAR2(30 CHAR);
e VARCHAR2(100);
f VARCHAR2(30 CHAR);
BEGIN
OPEN ref_curs FOR
SELECT
col1 ,
col2,
col3,
col4,
col5,
col6
FROM
my_table
WHERE
col1= = PARAM1 ;
DBMS_OUTPUT.PUT_LINE('col1 ,col2,col3,col4,col5,col6');
LOOP
FETCH ref_curs INTO a,b,c,d,e,f;
EXIT WHEN ref_curs%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(a||','|| b||','||c||','||d||','||e||','||f);
END LOOP;
CLOSE ref_curs;
END my_fun;

Related

unique constraint violated in stored procedure oracle

I have below stored procedure:
create or replace PROCEDURE CALCULATE_RECOVERY_HISTORY(p_month IN VARCHAR2) AS
l_id NUMBER;
BEGIN
ADD_LOG_INFO('CALCULATE_RECOVERY_HISTORY', 'Procedure Started');
l_id := SQ_AP_RECOVERY_HISTORY.NEXTVAL;
INSERT INTO t_ap_recovery_history (ID, RECOVERY_TARGET_MONTH, TARGET_INSTANCE, RECOVERY_PROGRESS, RECOVERY_TARGET, FAILED_TO_RECOVERY, FOCUS_AREA, IDENTIFIER_CLASS, CREATED_ON)
SELECT l_id,
a_recovery_target_month,
a_target_instance,
COUNT(CASE WHEN A_IS_RECOVERED = 'Y' THEN 1 END),
COUNT(CASE WHEN A_IS_RECOVERED IN ('Y', 'N') THEN 1 END),
COUNT(CASE WHEN A_IS_RECOVERED = 'N' THEN 1 END),
f.focus_area,
r.identifier_class,
SYSDATE
from t_ap_recovery_target t, t_ap_recovery_focusarea f, range r
where t.a_focus_area_id = f.id and t.a_range_id = r.id
and t.a_recovery_target_month = p_month
group by a_target_instance, a_recovery_target_month, f.focus_area, r.identifier_class;
COMMIT;
END CALCULATE_RECOVERY_HISTORY;
When I run the procedure, I get the error
ORA-00001: unique constraint violated.
I've also tried another way which is
SELECT SQ_AP_RECOVERY_HISTORY.NEXTVAL, a_recovery_target_month ...
But this also return another error which is
Sequence number not allowed here
What should I change in the code to solve this constraint issue?
Below is the table definition for T_AP_RECOVERY_HISTORY
CREATE TABLE "DIMSPST"."T_AP_RECOVERY_HISTORY"
( "ID" NUMBER(38,0),
"RECOVERY_TARGET_MONTH" VARCHAR2(6 BYTE) DEFAULT TO_CHAR(SYSTIMESTAMP, 'YYYYMM'),
"TARGET_INSTANCE" VARCHAR2(20 BYTE),
"RECOVERY_PROGRESS" NUMBER(38,0),
"RECOVERY_TARGET" NUMBER(38,0),
"FAILED_TO_RECOVERY" NUMBER(38,0),
"FOCUS_AREA" VARCHAR2(20 BYTE),
"IDENTIFIER_CLASS" VARCHAR2(42 BYTE),
"CREATED_ON" TIMESTAMP (6),
PRIMARY KEY ("ID")
Perform the aggregation in a sub-query and then apply the sequence value in an outer-query:
CREATE PROCEDURE CALCULATE_RECOVERY_HISTORY(
p_month IN VARCHAR2
)
AS
BEGIN
ADD_LOG_INFO('CALCULATE_RECOVERY_HISTORY', 'Procedure Started');
INSERT INTO t_ap_recovery_history (
ID,
RECOVERY_TARGET_MONTH,
TARGET_INSTANCE,
RECOVERY_PROGRESS,
RECOVERY_TARGET,
FAILED_TO_RECOVERY,
FOCUS_AREA,
IDENTIFIER_CLASS,
CREATED_ON
)
SELECT SQ_AP_RECOVERY_HISTORY.NEXTVAL,
a_recovery_target_month,
a_target_instance,
RECOVERY_PROGRESS,
RECOVERY_TARGET,
FAILED_TO_RECOVERY,
focus_area,
identifier_class,
SYSDATE
FROM (
SELECT a_recovery_target_month,
a_target_instance,
COUNT(CASE WHEN A_IS_RECOVERED = 'Y' THEN 1 END) AS RECOVERY_PROGRESS,
COUNT(CASE WHEN A_IS_RECOVERED IN ('Y', 'N') THEN 1 END) AS RECOVERY_TARGET,
COUNT(CASE WHEN A_IS_RECOVERED = 'N' THEN 1 END) AS FAILED_TO_RECOVERY,
f.focus_area,
r.identifier_class
FROM t_ap_recovery_target t
INNER JOIN t_ap_recovery_focusarea f
ON (t.a_focus_area_id = f.id)
INNER JOIN range r
ON (t.a_range_id = r.id)
WHERE t.a_recovery_target_month = p_month
GROUP BY
a_target_instance,
a_recovery_target_month,
f.focus_area,
r.identifier_class
);
END CALCULATE_RECOVERY_HISTORY;
/
Note: If you COMMIT in stored procedures then you cannot chain multiple procedures together and if one fails then ROLLBACK then all. Instead, you should COMMIT in the block that you call the procedures from.
fiddle
One option is to let Oracle create ID. You didn't specify database version you use, so trigger certainly is what would work:
create or replace trigger trg_bi_rec_hist
before insert on t_ap_recovery_history
for each row
begin
:new.id := SQ_AP_RECOVERY_HISTORY.NEXTVAL;
end;
/
Procedure then wouldn't contain insert into the ID column, i.e.
INSERT INTO t_ap_recovery_history (RECOVERY_TARGET_MONTH, ...)
SELECT a_recovery_target_month, ...
Another option (if your database version supports it) is to create ID as identity column instead of a trigger, e.g.
SQL> create table test
2 (id number generated always as identity);
Table created.
Or, if you would not create the trigger like in the previous answer, the procedure should look like below:
create or replace PROCEDURE CALCULATE_RECOVERY_HISTORY(p_month IN VARCHAR2) AS
l_id NUMBER;
BEGIN
ADD_LOG_INFO('CALCULATE_RECOVERY_HISTORY', 'Procedure Started');
INSERT INTO t_ap_recovery_history (ID, RECOVERY_TARGET_MONTH,
TARGET_INSTANCE, RECOVERY_PROGRESS, RECOVERY_TARGET,
FAILED_TO_RECOVERY, FOCUS_AREA, IDENTIFIER_CLASS, CREATED_ON)
with tb as (
SELECT a_recovery_target_month,
a_target_instance,
COUNT(CASE WHEN A_IS_RECOVERED = 'Y' THEN 1 END) c1,
COUNT(CASE WHEN A_IS_RECOVERED IN ('Y', 'N') THEN 1 END) c2,
COUNT(CASE WHEN A_IS_RECOVERED = 'N' THEN 1 END) c3,
f.focus_area,
r.identifier_class
from t_ap_recovery_target t, t_ap_recovery_focusarea f, range r
where t.a_focus_area_id = f.id and t.a_range_id = r.id
and t.a_recovery_target_month = p_month
group by a_target_instance, a_recovery_target_month,
f.focus_area, r.identifier_class
)
select SQ_AP_RECOVERY_HISTORY.NEXTVAL,
a_recovery_target_month,
a_target_instance,
c1,
c2,
c3,
focus_area,
identifier_class,
sysdate
from tb;
COMMIT;
END CALCULATE_RECOVERY_HISTORY;
In general triggers are detrimental to performance in case you have insert select inserting large numbers of rows in one go, or massive updates or massive deletes or merge.
If you have only DML affecting a small number of rows, triggers may save complexity, although I'd rather do more in stored procedures and less in triggers.

"ORA-22992: cannot use LOB locators selected from remote tables "

I have a query in oracle 19 which works fine:
select a.x,
b.y
from tab1#dblink a
join tab2#dblink b
on a.x = b.y
But when I create a view from this query, I have the error - "ORA-22992: cannot use LOB locators selected from remote tables".
Columns x and y are varchar2 not lobs.
Tables:
CREATE TABLE tab1
(
x VARCHAR2(4000 BYTE),
x2 CLOB,
x3 VARCHAR2(100 BYTE)
),
CREATE TABLE tab2
(
y VARCHAR2(4000 BYTE),
y2 CLOB,
y3 VARCHAR2(100 BYTE)
)
Do you have any idea why I have this error?
I want to know why query works fine and view doesn't.

Row compare and insert into log table only changed data

I'm trying to compare a global temporary table to another table and want to insert into a log table but can not seem to find the best/most efficient way to accomplish this.
Log Table
CREATE TABLE LogTable
(
Date_Time DATETIME,
Name VARCHAR2(10 CHAR),
old VARCHAR2(20 CHAR),
new VARCHAR2(20 CHAR),
)
Object Type
CREATE OR REPLACE type dbo.P_REC AS OBJECT
(
ATTR1 VARCHAR2(10 CHAR),
ATTR2 VARCHAR2(20 CHAR),
ATTR3 VARCHAR2(20 CHAR),
ATTR4 VARCHAR2(20 CHAR)
);
Collection Type
CREATE OR REPLACE type dbo.P_REC_LIST IS TABLE OF P_REC;
Stored Procedure
PROCEDURE PASSPEOPLETOORACLE(tmpCollection IN P_REC_LIST , resultCursor out sys_refcursor)
IS
BEGIN
IF tmpCollection .count > 0 THEN
INSERT INTO tmpPTbl SELECT * FROM table1; <--tmpPTbl is a copy of table1 before the merge statement.
MERGE INTO table1 MKTP
USING (
WITH tmpTBL AS
(
SELECT ADCOLL.ATTR1,
ADCOLL.ATTR2,
MV.ATTR3,
MV.ATTR4
FROM TABLE(tmpCollection) ADCOLL
LEFT JOIN materializedView MV
ON ADCOLL.ATTR1 = MV.ATTR1
)
SELECT DISTINCT COALESCE(tmpTBL.ATTR1,MKtmpTBL.ATTR1) AS ATTR1,
tmpTBL.ATTR2,
tmpTBL.ATTR3,
tmpTBL.ATTR4,
CASE WHEN tmpTBL.ATTR1 IS NULL
THEN 'Y' ELSE 'N' END
match_flag FROM tmpTBL
FULL JOIN table1 MKtmpTBL
ON MKtmpTBL.ATTR1 = tmpTBL.ATTR1
) usingTBL
ON (MKTP.ATTR1 = usingTBL.ATTR1)
WHEN MATCHED THEN
UPDATE SET MKTP.ATTR2 = usingTBL.ATTR2,
MKTP.ATTR3 = usingTBL.ATTR3,
MKTP.ATTR4 = usingTBL.ATTR4,
DELETE WHERE match_flag = 'Y'
WHEN NOT MATCHED THEN
INSERT (ATTR1)
VALUES (usingTBL.ATTR1);
END IF;
END;
Id like a way to compare the newly update records in table1 to the prior records in tmpPTbl and where the old and new values differ, insert a new row into the log table.
2019-02-14 23:59:59,jdoe,abcd,efgh would be an example of a record inserted into the log table.
tmpPTbl & table1 both have 50 columns in them & about 16k rows on average.
The best solution for you would be to create a Trigger on table Table1. So that any operation occurs on Table1 it can be logged to Logtable. See below demo:
CREATE TABLE table1
(col1 VARCHAR2(10),
col2 VARCHAR2(10),
col3 VARCHAR2(10) );
/
--Trigger
CREATE OR REPLACE TRIGGER Log_Entry before
INSERT OR
UPDATE ON table1 FOR EACH row
BEGIN
IF INSERTING THEN
INSERT INTO LogTable VALUES
(sysdate, :new.col1, :new.col2, :new.col3
);
ELSIF UPDATING THEN
INSERT INTO LogTable VALUES
(sysdate, :old.col1, :old.col2, :old.col3
);
END IF;
END;
/
Execution:
SQL> Insert into table1 values ('A','B','C');
SQL>Update table1
set col1 ='Z'
where col1 = 'A';
SQL> Merge INTO table1 tb1 USING
(SELECT 'Z' col1 , 'D' col2, 'K' col3 FROM dual
) tb2 ON (tb1.col1 = tb2.col1)
WHEN matched THEN
UPDATE SET tb1.col2=tb2.col2 WHEN NOT matched THEN
INSERT VALUES
(tb2.col1,tb2.col2,tb2.col3
);
SQL>Commit;
SQL> Select * from logtable;
DATE_TIME NAME OLD NEW
--------- ---------- -------------------- --------------------
15-FEB-19 A B C
15-FEB-19 Z B C
15-FEB-19 Z B C
Note there is no need to copy data to tmpPTbl table as well.

Retrieve Oracle Metadata with Internal Data

I am trying to write a PL/SL Procedure which uses both meta data and internal data of a table.It is like:
table1 (ABC varchar2(50),wsx varchar2(50));
table2 (ABC number(50),dv varchar2(50));
table3 (ABC varchar2(10),wsds varchar2(50));
table4 (ABC varchar2(20),wfsdg varchar2(50));
table5 (ABC number(50),wsxsfd varchar2(50));
All five tables have one column with same name 'ABC'.
Suppose table1 has 3 rows like
('JOHN.TEDA','avdv'),('MARK.LEE','fesf'),('JOHN.DEA','fwfd') and other table also have any data like this.
Now using column name as an input('ABC') i should get output as
attached.
we can get column info from user_tab_columns.
Max length means max length of existing data in column ::
select max(length(ABC)) from table1
I am getting problem in joining both
Tables are not referential.
I have tried to replicate the scenario as mentioned by you with the use of PIPELINED function in Oracle. Hope this helps.
CREATE OR REPLACE TYPE fun_obj
IS
OBJECT
(
tab_name VARCHAR2(100),
colname VARCHAR2(100),
datatyp VARCHAR2(100),
datlen NUMBER,
nullable VARCHAR2(1),
LEN NUMBER );
/
CREATE OR REPLACE
FUNCTION test_max_count(
colname IN VARCHAR2)
RETURN fun_tab PIPELINED
AS
tab fun_obj:=fun_obj(NULL,NULL,NULL,NULL,NULL,NULL);
lvlen NUMBER;
BEGIN
FOR I IN
(SELECT DISTINCT table_name,
OWNER,
COLUMN_NAME,
DATA_TYPE,
DATA_LENGTH,
NULLABLE,
NULL AS MAX_LEN
FROM all_tab_columns
WHERE column_name = colname
)
LOOP
tab.tab_name:=i.table_name;
tab.colname :=i.COLUMN_NAME;
tab.datatyp :=i.DATA_TYPE;
tab.datlen :=i.DATA_LENGTH;
tab.nullable:=i.NULLABLE;
EXECUTE IMMEDIATE 'SELECT MAX(LENGTH('||i.column_name||')) FROM '||I.OWNER||'.'||I.TABLE_NAME INTO lvlen;
tab.len:=lvlen;
PIPE ROW(tab);
END LOOP;
END;
/
------------------------------------To Execute----------------------------------
SELECT * FROM TABLE(test_max_count('abc'));
CREATE OR REPLACE PROCEDURE test2 ( p_column_name IN varchar )
IS
CURSOR GET_DATA (COL VARCHAR )IS
SELECT TABLE_NAME ,COLUMN_NAME,DATA_TYPE,DATA_LENGTH,NULLABLE
FROM
user_tab_columns where COLUMN_NAME = COL;
a_table varchar2(50);
B_COL varchar2(50);
a_max varchar2(50);
BEGIN
FOR C IN GET_DATA(p_column_name) LOOP
a_table := c.table_name;
B_COL := C.COLUMN_NAME;
EXECUTE IMMEDIATE 'SELECT MAX(LENGTH('||B_COL||')) FROM '||a_table into a_max ;
insert into received_Data values (c.table_name,C.COLUMN_NAME,C.DATA_TYPE,C.DATA_LENGTH,C.NULLABLE,a_max);
END LOOP;
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR (-20001,
p_column_name || ':$:' || SQLERRM, TRUE) ;
END test2;
/
CREATE TABLE RECEIVED_DATA
( TABLE_NAME VARCHAR2(50 BYTE),
COLUMN_NAME VARCHAR2(50 BYTE),
DATA_TYPE VARCHAR2(50 BYTE),
DATA_LENGTH VARCHAR2(50 BYTE),
IS_NULL VARCHAR2(50 BYTE),
MAX_LENGTH VARCHAR2(50 BYTE));
You cannot achieve this directly with SQL. You need PL/SQL + Execute Immediate to do the job.
create a table with your expected columns
then use for each loop to count the max length on each column.

ORACLE SQL ORA-22814 attribute or element value is larger than specified in type

I'm calling a function that returns a type of table.
My function receives a variable of the type varchar. Because i return a type that i created i had to make cast and multiset...
My problems is that, the select inside my multiset executes without problem outside of the function, but when i execute my function it gives me ORA-22814 attribute or element value is larger than specified in type error.
NOTE: i always run the script with the same variable value
Type OBJECT:
create or replace
TYPE Z_PEDIDO_SeB IS OBJECT(
Contrato varchar2(4000 BYTE),
DataObjectivo date,
DataObjectivoSpecified date,
Descricao varchar2(500 BYTE),
DireccaoRequerente varchar2(50 BYTE),
Empresa varchar2(50 BYTE),
Estado varchar2(50 BYTE),
GestorDDS varchar2(100 BYTE),
GestorEncomenda varchar2(30 BYTE),
Referencia varchar2(50 BYTE),
Link varchar2(500 BYTE),
ObsComite varchar2(4000 BYTE),
OutrosFornecedores varchar2(4000 BYTE),
OutrosSistAfectados varchar2(4000 BYTE),
PrincipalSistAfectado varchar2(4000 BYTE),
Prioridade varchar2(50 BYTE),
Rede varchar2(50 BYTE),
Requerente varchar2(100 BYTE),
TestesAceitacao varchar2(10 BYTE),
proj_id varchar2(50 BYTE));
Type TABLE:
create or replace
TYPE Z_TABLE_PEDIDO_SeB IS TABLE OF Z_PEDIDO_SeB;
FUNCTION:
create or replace
function Z_GetDadosCreateUpdate_SEB(
proj_id in varchar2
)
return Z_TABLE_PEDIDO_SeB as
t_dados Z_TABLE_PEDIDO_SeB;
begin
select
cast(
multiset(
--STARTS HERE WHAT I RUN WITHOUT PROBLEM OUTSIDE THE FUNCTION
select
(SELECT line_text
from long_text
where key2 = proj_id and key1 = 'contrato'
) Contrato,
NVL(SCHEDULE_FINISH,ACTUAL_FINISH) DataObjectivo,
NVL(SCHEDULE_FINISH,ACTUAL_FINISH) DataObjectivoSpecified,
pedidos.description as Descricao,
costum.direcaorequerente DireccaoRequerente,
costum.empresa Empresa,
estado.description Estado,
costum.gestordds GestorDDS,
(select recursos.description
from structure recursos,
resources,
workflow,
wf_team
where recursos.structure_code = resources.resource_code
and workflow.planning_code = projectos.structure_code
and wf_team.workflow_id = workflow.workflow_id
and wf_team.lifecycle_role_code = '868'
and wf_team.user_name = resources.LOGON_ID
and rownum = 1
) GestorEncomenda,
pedidos.structure_code ID,
(SELECT line_text
from long_text
where key2 = proj_id and key1 = 'urlcadernoreq'
) Link,
(SELECT line_text
from long_text
where key2 = proj_id and key1 = 'observacoescomite'
) ObsComite,
(SELECT line_text
from long_text
where key2 = proj_id and key1 = 'outrosfornecedores'
) OutrosFornecedores,
(SELECT line_text
from long_text
where key2 = proj_id and key1 = 'outrossistemas'
) OutrosSistAfectados,
(SELECT line_text
from long_text
where key2 = proj_id and key1 = 'principalsistema'
) PrincipalSistAfectado,
costum.prioridade Prioridade,
(SELECT rede.description
from structure rede, planning_entity proj
where proj.code88 = rede.structure_code
and proj.planning_code = proj_id
) Rede,
costum.requerente Requerente,
(SELECT rede.description
from structure rede, planning_entity proj
where proj.code89 = rede.structure_code
and proj.planning_code = proj_id
) TestesAceitacao,
projectos. structure_code proj_id
from structure projectos,
planning_entity,
structure pedidos,
structure estado,
custom_data costum
where projectos.structure_code = planning_entity.planning_code
and planning_entity.planning_code = planning_entity.ppl_code
and pedidos.structure_code = planning_entity.code31
and estado.structure_code = planning_entity.code20
and projectos.structure_code = proj_id
and costum.planning_code = proj_id
-- HERE ENDS WHAT I RUN OUTSIDE THE FUNCTION WITHOUT A PROBLEM
)
as Z_TABLE_PEDIDO_SeB)
into
t_dados
from
dual;
return t_dados;
end Z_GetDadosCreateUpdate_SEB;
What i execute:
SELECT * FROM table (PVDEV.Z_GetDadosCreateUpdate_SEB('184765'));
Error i got:
ORA-22814: valor do atributo ou elemento é superior ao especificado no tipo
ORA-06512: na "PVDEV.Z_GETDADOSCREATEUPDATE_SEB", linha 7
22814. 00000 - "attribute or element value is larger than specified in type"
*Cause: Value provided for the object type attribute or collection element
exceeded the size specified in the type declaration.
*Action: Choose another value and retry the operation.
NOTE: if i try 18476 instead of 184765 it runs with no problem. So? how did i put such restriction? Where?
NOTE: Now i'm showing the error, the real one, i'm really sorry for the mistake
I would really appreciate any answer as other's people work is waiting for my part :S. Anyway thanks in advance for any information.
If I were you I would declared the function as the pipelined one and returned values by pipe row statement. And surely get rid of CAST/multiset. For loop statement is your choice. I'm sure it will work using my piece of advice.