Select values from table and update them using function in Oracle - sql

I need to set branch_code as IN parameter and then select cms_rungno, cms_finayear from table and assign cms_rungno to a variable and update its value by 1. Then, return the journ_no by below format.
Here's my code I have tried so far. I have no clear idea about this.
FUNCTION GEN_JOURNO
( branch_code IN varchar2 )
RETURN varchar2 IS
journ_no varchar2;
rungno number;
BEGIN
--LAST_NO + 1
SELECT cms_finayear
FROM corpinfo.tblcommonserial a
where cms_brncode=branch_code and cms_code='JOUN'
SELECT cms_rungno INTO rungno
FROM corpinfo.tblcommonserial a
where cms_brncode:=branch_code and cms_code:='JOUN';
UPDATE corpinfo.tblcommonserial a
set cms_rungno:=rungno+1
where cms_brncode:=branch_code and cms_code:='JOUN'
journ_no:=cms_brncode || SUBSTR(TO_CHAR(cms_finayear),3,2)|| LPAD(TO_CHAR(cms_rungno),6,'0');
RETURN journ_no ;
--EXCEPTION
--WHEN exception_name THEN
-- statements ;
END;

This worked for me. Had syntax mistakes in my previous code
FUNCTION GEN_JOURNO
( branch_code IN varchar2 )
RETURN varchar2(5) IS
journ_no varchar2(5);
rungno number;
BEGIN
--LAST_NO + 1
SELECT cms_finayear
FROM corpinfo.tblcommonserial a
where cms_brncode=branch_code and cms_code='JOUN';
SELECT cms_rungno INTO rungno
FROM corpinfo.tblcommonserial a
where cms_brncode=branch_code and cms_code='JOUN';
UPDATE corpinfo.tblcommonserial a
set cms_rungno=rungno+1
where cms_brncode=branch_code and cms_code='JOUN';
journ_no=branch_code || SUBSTR(TO_CHAR(cms_finayear),3,2)|| LPAD(TO_CHAR(cms_rungno),6,'0');
RETURN journ_no ;
--EXCEPTION
--WHEN exception_name THEN
-- statements ;
END;

Related

Passing column name as parameter in Oracle PL_SQL function

I am trying to pass two input parameters, column name and table name . Then the function should output a string value as defined below :
create or replace function get_id5(in_col_name IN VARCHAR2,in_tbl_name IN VARCHAR2)
return VARCHAR2
is
/*in_col_nm varchar(32) := in_col_name;
/*in_tbl_nm varchar(64) := in_tbl_name; */
integer_part NUMBER ;
integer_part_str VARCHAR2(32) ;
string_part VARCHAR2(32) ;
full_id VARCHAR2(32) ;
out_id VARCHAR(32) ;
BEGIN
/*select MAX(in_col_nm) INTO full_id FROM in_tbl_nm ; */
execute immediate 'select MAX('||in_col_name||') FROM' || in_tbl_name ||'INTO' || full_id;
/*select regexp_replace(full_id , '[^0-9]', '') INTO integer_part_str , regexp_replace(full_id , '[^a-z and ^A-Z]', '') INTO string_part from dual ; */
integer_part_str := regexp_replace(full_id , '[^0-9]', '') ;
string_part := regexp_replace(full_id , '[^a-z and ^A-Z]', '') ;
integer_part := TO_NUMBER(integer_part_str);
integer_part := integer_part + 1 ;
integer_part_str := TO_CHAR(integer_part) ;
out_id := string_part + integer_part_str ;
return out_id;
END;
I have a table named BRANDS in Database and the max value for column BRAND_ID is 'Brand05'. The expected output is 'Brand06'.
However when i run:
select get_id5('BRAND_ID' , 'BRANDS') from dual;
or
DECLARE
a VARCHAR(32) ;
BEGIN
a := get_id5('BRAND_ID' , 'BRANDS');
END;
I am getting the below error:
ORA-00923: FROM keyword not found where expected
You need spaces between the FROM and the table_name and the INTO should be outside of the SQL text:
execute immediate 'select MAX('||in_col_name||') FROM ' || in_tbl_name INTO full_id;
You could also use DBMS_ASSERT:
execute immediate 'select MAX('||DBMS_ASSERT.SIMPLE_SQL_NAME(in_col_name)||') FROM ' || DBMS_ASSERT.SIMPLE_SQL_NAME(in_tbl_name) INTO full_id;
Then you need to use || as the string concatenation operator (rather than +).
Your function could be:
create or replace function get_id5(
in_col_name IN VARCHAR2,
in_tbl_name IN VARCHAR2
) RETURN VARCHAR2
IS
full_id VARCHAR2(32);
BEGIN
execute immediate 'select MAX('||DBMS_ASSERT.SIMPLE_SQL_NAME(in_col_name)||') '
|| 'FROM ' || DBMS_ASSERT.SIMPLE_SQL_NAME(in_tbl_name)
INTO full_id;
return regexp_replace(full_id , '\d+', '')
|| TO_CHAR(regexp_replace(full_id , '\D+', '') + 1);
END;
/
For the sample data:
CREATE TABLE brands (brand_id) AS
SELECT 'BRAND5' FROM DUAL;
Then:
select get_id5('BRAND_ID' , 'BRANDS') AS id from dual;
Outputs:
ID
BRAND6
db<>fiddle here
A better solution
To generate the number, use a SEQUENCE or, from Oracle 12, an IDENTITY column and, if you must have a string prefix then use a virtual column to generate it.
CREATE TABLE brands(
ID NUMBER
GENERATED ALWAYS AS IDENTITY
PRIMARY KEY,
brand_id VARCHAR2(10)
GENERATED ALWAYS
AS (CAST('BRAND' || TO_CHAR(id, 'FM000') AS VARCHAR2(10)))
);
BEGIN
INSERT INTO brands (id) VALUES (DEFAULT);
INSERT INTO brands (id) VALUES (DEFAULT);
INSERT INTO brands (id) VALUES (DEFAULT);
END;
/
SELECT * FROM brands;
Outputs:
ID
BRAND_ID
1
BRAND001
2
BRAND002
3
BRAND003
db<>fiddle here

passing variable value in pl/sql query block

I struck up with one issue. please need your expertise..
PL/SQL block contain a variable p_user_id varchar(50)
The value set to below value
p_user_id := '101,102,103';
I have query like below in PL/SQl block
select Count(*) into v_count
from users
where user_id not in (p_user_id);
Every time when I call this PL/SQL block v_count value is same i.e. total no of records in users table. Not In clause not working properly. Please help.
Using LIKE string comparison:
DECLARE
p_user_id VARCHAR2(200) := '101,102,103';
v_count INT;
BEGIN
SELECT Count(*)
INTO v_count
FROM users
WHERE ',' || p_user_id || ',' NOT LIKE '%,' || user_id || ',%';
END;
/
Using collections:
CREATE OR REPLACE TYPE intlist IS TABLE OF INT;
/
DECLARE
p_user_id INTLIST := INTLIST( 101, 102, 103 );
v_count INT;
BEGIN
SELECT Count(*)
INTO v_count
FROM users
WHERE user_id NOT MEMBER OF p_user_id;
END;
/
There are no rows in the users table where user_id has the value '101,102,103'.
If you want to split a string into a set of separate values, you have to do a bit more than just use it in a not in expression.
Some possible approaches:
declare
p_csvlist varchar2(100) := '2002, 7369, 7499, 7902, 7934';
v_count integer;
begin
select count(*) into v_count
from emp e
where e.empno in
( select extractvalue(xt.column_value,'e')
from table(xmlsequence
( extract
( xmltype('<coll><e>' || replace(p_csvlist,',','</e><e>') || '</e></coll>')
, '/coll/*') )) xt );
dbms_output.put_line(v_count || ' rows');
end;
or this
declare
p_csvlist varchar2(100) := '2002, 7369, 7499, 7902, 7934';
v_count integer;
begin
select count(*) into v_count
from emp e
where e.empno in
( select regexp_substr(p_csvlist, '[^,]+',1,rownum)
from dual
connect by rownum <= length(p_csvlist) - length(replace(p_csvlist,',')) );
dbms_output.put_line(v_count || ' rows');
end;
or this (only works with numeric values):
declare
p_csvlist varchar2(100) := '2002, 7369, 7499, 7902, 7934';
v_count integer;
begin
select count(*) into v_count
from emp e
where e.empno in
( select to_number(xt.column_value)
from xmltable(p_csvlist) xt );
dbms_output.put_line(v_count || ' rows');
end;
Examples are from my FAQ article:
www.williamrobertson.net/documents/comma-separated.html
You can use sys.DBMS_DEBUG_VC2COLL --collection datatype provided by Oracle itself
DECLARE
p_user_id sys.DBMS_DEBUG_VC2COLL := sys.DBMS_DEBUG_VC2COLL( 101, 102, 103 );
v_count INT;
BEGIN
SELECT Count(*)
INTO v_count
FROM users
WHERE user_id not in(select * from table(p_user_id));
END;
/

Can we pass partition as parameter in stored procedure ? I'm getting following error

Can we pass partition as parameter in stored procedure ? I'm getting following error.
Please help on this.
CREATE OR REPLACE PROCEDURE PROCEDURE_NAME (PARTITION_NAME IN VARCHAR2)
IS
LASTDATE VARCHAR2(12);
ENDDATE VARCHAR2(12);
BEGIN
IF PARTITION_NAME='DEC_2014' OR PARTITION_NAME='JAN_2015' OR PARTITION_NAME='MAR_2015' OR PARTITION_NAME='MAY_2015' OR PARTITION_NAME='JUL_2015' OR PARTITION_NAME='AUG_2015' OR PARTITION_NAME='OCT_2015' OR PARTITION_NAME='DEC_2015' OR PARTITION_NAME='JAN_2016' THEN
ENDDATE:='31';
ELSIF PARTITION_NAME='NOV_2014' OR PARTITION_NAME='APR_2015' OR PARTITION_NAME='JUN_2015' OR PARTITION_NAME='SEP_2015' OR PARTITION_NAME='NOV_2015' THEN
ENDDATE:='30';
ELSE
ENDDATE:='28';
END IF;
LASTDATE:=CONCAT(CONCAT(ENDDATE,'-'),REPLACE (PARTITION_NAME, '_', '-'));
DBMS_OUTPUT.PUT_LINE(LASTDATE);
DBMS_OUTPUT.PUT_LINE(PARTITION_NAME);
UPDATE
/*+ PARALLEL(Alias 4) */
TABLE_NAME PARTITION (PARTITION_NAME) Alias
SET Alias.Alias_D_EFFECTIVE_DATE =
(SELECT
/*+ PARALLEL(Alias3 4) */
ALAIS2.ALAIS2_D_DATETIME
FROM schema1.TABLE_2 ALAIS2
WHERE TRUNC(ALAIS2_D_DATETIME) <= TO_DATE(LASTDATE,'DD-MON-YYYY')
AND ALAIS2.ALAIS2_N_TRN_ID = Alias.Alias_N_PR
)
WHERE EXISTS
(SELECT Alias3.ALAIS2_D_DATETIME
FROM schema1.TABLE_2 Alias3
WHERE TRUNC(Alias3.ALAIS2_D_DATETIME) <= TO_DATE(LASTDATE,'DD-MON-YYYY')
AND Alias3.ALAIS2_N_TRN_ID = Alias.Alias_N_PR
);
COMMIT;
END PROCEDURE_NAME;
Error:
Error at line 1
ORA-02149: Specified partition does not exist
ORA-06512: at "schema1.PROCEDURE_NAME", line 17
ORA-06512: at line 1
I think you can do by Dynamic SQL Statements, EXECUTE IMMEDIATE Statement
follow this link ...
https://docs.oracle.com/cd/B10501_01/appdev.920/a96590/adg09dyn.htm
https://docs.oracle.com/cloud/latest/db112/LNPLS/dynamic.htm#LNPLS01115
CREATE OR REPLACE PROCEDURE PROCEDURE_NAME (PARTITION_NAME IN VARCHAR2)
IS
LASTDATE VARCHAR2(12);
ENDDATE VARCHAR2(12);
query_str VARCHAR2(1000);
BEGIN
//.......your code.......
query_str := 'UPDATE TABLE_NAME PARTITION (' || PARTITION_NAME || ') Alias '
||'SET Alias.Alias_D_EFFECTIVE_DATE =.....'
EXECUTE IMMEDIATE query_str;
COMMIT;
END PROCEDURE_NAME;
Hope this will work.

Trigger compilation error

I need some help in creating a trigger.
create or replace trigger trigger_one
before insert on Funtom_timesheet
for each row
Declare
V_id number;
V_hours number;
Begin
Select max(timesheet_ID)+1 into v_id from Funtom_timesheet
:new.timesheet_ID :=v_id;
select grade_hours into V_hours
from funtom_grade join funtom_employee
on emp_grade = grade_id
where empid = :new.timesheet_emp;
if V_hours >:new.timesheet_hours
else
:new.timesheet_overtime :=
:new.timesheet_hours-V_hours
:new.timesheet_hours:= V_hours;
END IF;
END;
/
please tell me which part of my code is wrong so I could work on it,
Thanks
You have many syntax errors - missing ; and then. There can't be if only with else part and without expression on it.
CREATE OR REPLACE TRIGGER TRIGGER_ONE
BEFORE INSERT ON FUNTOM_TIMESHEET
FOR EACH ROW
DECLARE
V_ID NUMBER;
V_HOURS NUMBER;
BEGIN
SELECT MAX(TIMESHEET_ID) + 1 INTO V_ID FROM FUNTOM_TIMESHEET;
:NEW.TIMESHEET_ID := V_ID;
SELECT GRADE_HOURS
INTO V_HOURS
FROM FUNTOM_GRADE
JOIN FUNTOM_EMPLOYEE
ON EMP_GRADE = GRADE_ID
WHERE EMPID = :NEW.TIMESHEET_EMP;
IF V_HOURS > :NEW.TIMESHEET_HOURS THEN
NULL;
ELSE
:NEW.TIMESHEET_OVERTIME := :NEW.TIMESHEET_HOURS - V_HOURS;
:NEW.TIMESHEET_HOURS := V_HOURS;
END IF;
END;
/
Also better use SEQUENCES instead of:
SELECT MAX(TIMESHEET_ID) + 1 INTO V_ID FROM FUNTOM_TIMESHEET;
:NEW.TIMESHEET_ID := V_ID;
When selecting form the same table as inserting, you can get MUTATING trigger error (http://www.dba-oracle.com/t_avoiding_mutating_table_error.htm)

Overlap function in Oracle

I'm writing a package to learn Oracle. I want to create an OVERLAP function that checks if two date ranges overlap each other.
FUNCTION OVERLAP(p_START_DATE_1 DATE, p_END_DATE_1 DATE,
p_START_DATE_2 DATE, p_END_DATE_2 DATE) RETURN VARCHAR2 AS
lv_RESULT VARCHAR2(1);
BEGIN
lv_RESULT := SELECT 'T' AS overlap FROM dual
WHERE (p_START_DATE_1, p_END_DATE_1) overlaps (p_START_DATE_2, p_END_DATE_2);
IF (lv_RESULT = 'T')
RETURN 'T';
RETURN 'N';
END OVERLAP;
I tried to execute my function, but getting an error ORA-04063: package body 'XYZ' contains errors...
SELECT KP_XYZ_PACKAGE_SQL.OVERLAP(
TO_DATE('01/01/2014', 'DD/MM/YYYY'),
TO_DATE('01/12/2014', 'DD/MM/YYYY'),
TO_DATE('01/02/2014', 'DD/MM/YYYY'),
TO_DATE('01/05/2014', 'DD/MM/YYYY'))
FROM DUAL;
I think SELECT works fine. But the error occurs (I suppose) here: lv_RESULT := SELECT.... Why?
Try this:
CREATE OR REPLACE FUNCTION OVERLAP(p_START_DATE_1 DATE, p_END_DATE_1 DATE,
p_START_DATE_2 DATE, p_END_DATE_2 DATE) RETURN VARCHAR2 AS
lv_RESULT VARCHAR2(1);
BEGIN
lv_RESULT := 'N';
SELECT 'T' into lv_RESULT FROM dual
WHERE (p_START_DATE_1, p_END_DATE_1) overlaps (p_START_DATE_2, p_END_DATE_2);
IF (lv_RESULT = 'T') THEN
RETURN 'T';
END IF;
RETURN 'N';
END OVERLAP;
The IF statement was incomplete too - THEN and END IF were missing, which I have now added.
The documentation for SELECT INTO statement is here. There are links to examples at the end of the page.
Corrected version : return 'T' for True 'N' for False (?)
CREATE OR REPLACE FUNCTION OVERLAP(p_START_DATE_1 DATE, p_END_DATE_1 DATE,
p_START_DATE_2 DATE, p_END_DATE_2 DATE) RETURN VARCHAR2 AS
lv_RESULT VARCHAR2(1);
BEGIN
SELECT 'T' into lv_RESULT FROM dual WHERE (p_START_DATE_1, p_END_DATE_1) overlaps (p_START_DATE_2, p_END_DATE_2);
RETURN lv_RESULT;
EXCEPTION
WHEN NO_DATA_FOUND THEN lv_RESULT := 'N';
RETURN lv_RESULT;
END OVERLAP;
/