Is there any alternate way to write this query plsql? - oracle

I want to write this code in one select query without if statement, because its looks like i'm repeating the code.
Any idea?
if p_code is null then
select *
into v_row
from group_matrix gm
where gm.group_code= p_group_code
and gm.code is null;
else
select *
into v_row
from group_matrix gm
where gm.group_code= p_group_code
and gm.code = p_code ;
end if;

The simplest way is to create the condition to check p_code and gm.code, both are null or both are same as follows:
select *
into v_row
from group_matrix gm
where gm.group_code= p_group_code
and ( (p_code is null and gm.code is null)
or gm.code = p_code )

I believe the most readable way is with some boolean logic:
select *
into v_row
from group_matrix gm
where gm.group_code= p_group_code
and ( (p_code is null and gm.code is null)
or
(p_code is not null and gm.code = p_code)
)
This can be re-written in different more compact ways, but this one is quite self explaining.

To me, it looks as
select *
into v_row
from group_matrix gm
where gm.group_code = p_group_code
and (gm.code = p.code or p_code is null);

you can use dynamic query as well
if p_code is null then
abc:=' gm.code is null'
else
abc:=' gm.code = p_code'
end if
vsql := 'select * into v_rowfrom group_matrix gm where gm.group_code= p_group_code and'||abc;

select *
into v_row
from group_matrix gm
where gm.group_code = p_group_code
and decode(p_code, gm.code, 1, 2) = 1

Related

Inserting rowtype record, which is passed as a parameter in PL/SQL in Oracle. How can I achieve this?

I am trying to achieve below scenario. How can I achieve below insert given below?
declare
v_insSql varchar2(1000);
v_table_name varchar2(50):='TEMP_TBL';
v_row Table_T1%ROWTYPE;
/* Just to avoid everyone's confusion, both table_t1 and temp_tbl are of same structure */
BEGIN
SELECT * INTO v_row FROM Table_T1 WHERE ROWNUM = 1;
v_insSql:= 'INSERT INTO '||v_table_name||' VALUES :l_row';
/* table_name will always be passed as variable */
EXECUTE IMMEDIATE v_insSql using v_row;
END;
Can someone pls help?
Don't use dynamic SQL; just use a normal SQL insert:
DECLARE
v_insSql varchar2(1000);
v_table_name varchar2(50):='TEMP_TBL';
v_row Table_T1%ROWTYPE;
BEGIN
SELECT * INTO v_row FROM Table_T1 WHERE ROWNUM = 1;
INSERT INTO temp_tbl VALUES v_row;
END;
/
So, for some sample tables/data:
CREATE TABLE table_t1 ( a NUMBER, b NUMBER, c NUMBER );
INSERT INTO table_t1 ( a, b, c ) VALUES ( 1, 2, 3 );
CREATE TABLE temp_tbl AS SELECT * FROM table_t1 WHERE 1 = 0;
Then:
SELECT * FROM temp_tbl;
Outputs:
A | B | C
-: | -: | -:
1 | 2 | 3
db<>fiddle here
If you want to have dynamic table names then white-list the tables so you don't need to use dynamic SQL:
DECLARE
v_insSql varchar2(1000);
v_table_name varchar2(50):='TEMP_TBL';
v_row Table_T1%ROWTYPE;
BEGIN
SELECT * INTO v_row FROM Table_T1 WHERE ROWNUM = 1;
IF v_table_name = 'TEMP_TBL' THEN
INSERT INTO temp_tbl VALUES v_row;
ELSIF v_table_name = 'TABLE_T1' THEN
INSERT INTO table_t1 VALUES v_row;
END IF;
END;
/
db<>fiddle here
You can get what your after but with the intermediate step of defining and filling a %rowtype variable. Since the source table (Table_T1) and the target table (Temp_Tbl) must have identical structure you insert directly for select.
declare
k_sql_stmt constant varchar2(100) := 'insert into <table_name> select * from table_t1 where rownum=1';
l_sql_stmt varchar2(100);
l_table_name varchar2(30) := 'temp_tbl';
begin
l_sql_stmt := replace(k_sql_stmt,'<table_name>',l_table_name);
dbms_output.put_line ('Run Statement==> ''' || l_sql_stmt || '''');
execute immediate l_sql_stmt;
end ;

ORA-01861: literal does not match format string.while executing procedure

I have written procedure which have date paramters defined as below:
in_Spendpaidstartdt IN DATE,
in_Spendpaidenddt IN DATE,
while with in procedure i am calling these paramters as:
AND ( in_Spendpaidstartdt IS NULL
OR err.Spendpaiddt >= in_Spendpaidstartdt)
AND ( in_Spendpaidenddt IS NULL
OR err.Spendpaiddt <= in_Spendpaidenddt));
however oracle is giving following error:
"ORA-01861: literal does not match format string"
Some one please suggest the work around.
Here is dummy:
CREATE OR REPLACE PROCEDURE XYZ (
in_startdt IN DATE,
in_enddt IN DATE,
output OUT SYS_REFCURSOR)
IS
rcrdnums VARCHAR2 (32767);
rcrd_cnt INT;
BEGIN
rcrd_cnt := 500;
SELECT RTRIM (
XMLCAST (
XMLAGG (XMLELEMENT (e, RCRDNUM) ORDER BY RCRDNUM) AS CLOB),
',')
INTO rcrdnums
FROM (SELECT (ERR.RCRDNUM || ',') AS RCRDNUM
FROM Table_NAME ERR
WHERE ROWNUM <= rcrd_cnt
and ( in_startdt IS NULL
OR to_date(err.paiddt, 'dd/mm/yyyy') >= to_date(in_startdt, 'dd/mm/yyyy'))
AND ( in_enddt IS NULL
OR to_date(err.paiddt, 'dd/mm/yyyy') <= to_date(in_enddt, 'dd/mm/yyyy')));
IF LENGTH (rcrdnums) = 1
THEN
rcrdnums := NULL;
ELSE
rcrdnums := rcrdnums;
--SUBSTR (rcrdnums, 1, LENGTH (rcrdnums) - 1);
END IF;
DBMS_OUTPUT.PUT_LINE (rcrdnums);
OPEN outputFOR
SELECT *
FROM Table_NAME ERR
INNER JOIN ( SELECT REGEXP_SUBSTR (rcrdnums,
'[^,]+',
1,
LEVEL)
AS EVENT
FROM DUAL
CONNECT BY REGEXP_SUBSTR (rcrdnums,
'[^,]+',
1,
LEVEL)
IS NOT NULL) EVENT_P
ON EVENT_P.EVENT = ERR.RCRDNUM;
END;
/
As already mentioned to #XING, your issues are two-fold.
You are forcing Oracle to do an implicit conversion of a DATE back to a string, when you used to_date on something that's already a DATE - something I've already mentioned elsewhere on stackoverflow!
You are (probably) not passing in the parameters correctly when calling your procedure.
Here is how I'd amend your procedure:
CREATE OR REPLACE PROCEDURE XYZ (
in_startdt IN DATE,
in_enddt IN DATE,
output OUT SYS_REFCURSOR)
IS
rcrdnums VARCHAR2 (32767);
rcrd_cnt INT;
BEGIN
rcrd_cnt := 500;
SELECT RTRIM (
XMLCAST (
XMLAGG (XMLELEMENT (e, RCRDNUM) ORDER BY RCRDNUM) AS CLOB),
',')
INTO rcrdnums
FROM (SELECT (ERR.RCRDNUM || ',') AS RCRDNUM
FROM Table_NAME ERR
WHERE ROWNUM <= rcrd_cnt
and ( in_startdt IS NULL
OR to_date(err.paiddt, 'dd/mm/yyyy') >= in_startdt) -- in_startdt is already a DATE, so no need to convert it
AND ( in_enddt IS NULL
OR to_date(err.paiddt, 'dd/mm/yyyy') <= in_enddt)); -- in_enddt is already a DATE, so no need to convert it
IF LENGTH (rcrdnums) = 1
THEN
rcrdnums := NULL;
ELSE
rcrdnums := rcrdnums;
--SUBSTR (rcrdnums, 1, LENGTH (rcrdnums) - 1);
END IF;
DBMS_OUTPUT.PUT_LINE (rcrdnums);
OPEN output FOR
SELECT *
FROM Table_NAME ERR
INNER JOIN ( SELECT REGEXP_SUBSTR (rcrdnums,
'[^,]+',
1,
LEVEL)
AS EVENT
FROM DUAL
CONNECT BY REGEXP_SUBSTR (rcrdnums,
'[^,]+',
1,
LEVEL)
IS NOT NULL) EVENT_P
ON EVENT_P.EVENT = ERR.RCRDNUM;
END;
/
And to test, I'd call your procedure like so:
declare
v_refcur sys_refcursor;
begin
xyz(in_startdt => to_date('01/10/2016', 'dd/mm/yyyy'),
in_enddt => to_date('05/10/2016', 'dd/mm/yyyy'),
output => v_refcur);
end;
/
N.B. it's bad practice to use "select *" in production code - you should explicitly specify the columns you're wanting to get back; that way, if someone adds a column, your code won't cause something to break because it won't pass that extra column along.

Oracle select statement based on multiple input parameters

How I achieve the following in oracle 11g.
CREATE OR REPLACE PROCEDURE sel_tblStateMaster (
p_id IN NUMBER DEFAULT NULL,
p_code IN varchar DEFAULT NULL,
p_name IN varchar DEFAULT NULL),
p_result OUT sys_refcursor
as
begin
// Dummy code
OPEN p_result FOR SELECT * FROM TBLSTATEMASTER;
if not p_code IS NULL
OPEN p_result FOR SELECT * FROM p_result where code=p_code;
end if;
if not p_name IS NULL
OPEN p_result FOR SELECT * FROM p_result where name=p_name;
end if;
end
/
I know above specified query is not correct, I just need a corrected solution for Oracle.
It is a simple IF-ELSE construct:
IF p_code IS NOT NULL AND p_name IS NOT NULL
THEN
OPEN p_result FOR SELECT * FROM TBLSTATEMASTER WHERE code=p_code AND NAME=p_name;
ELSE
OPEN p_result FOR SELECT * FROM TBLSTATEMASTER;
END IF;
The IF statement should have a then clause
CREATE OR REPLACE PROCEDURE sel_tblStateMaster (
p_id IN NUMBER DEFAULT NULL,
p_code IN varchar DEFAULT NULL,
p_name IN varchar DEFAULT NULL),
p_result OUT sys_refcursor
as
begin
// Dummy code
OPEN p_result FOR SELECT * FROM TBLSTATEMASTER;
if not p_code IS NULL
then
OPEN p_result FOR SELECT * FROM p_result where code=p_code;
end if;
if not p_name IS NULL
then
OPEN p_result FOR SELECT * FROM p_result where name=p_name;
end if;
end;

How to declare a Cursors With different conditions

I have a procedure EMPHIRESEPCHAN which is used to fetch the employees list who are hired, seperated and changed their titles based on a particular time frame. The procedure is as follows:
PROCEDURE EMPHIRESEPCHAN ( p_Start in VarChar2, p_End in VarChar2,
p_Hire IN VarChar2, p_Sep IN VarChar2, p_Changed IN VarChar2, p_Condition1 IN VarChar2, p_Condition2 IN VarChar2)
IS
CURSOR c_emplst ( p_listtype varchar2 ) IS
select e.emp_id, e.name, e.Rank
from person.emp e
where emp_id in (select distinct(emp_id) from person.promo
where pdate between p_startDate and p_endDate
and dcode in
(select adj from support.descr where typ = 'PROMO' and smeaning = p_listtype) );
CURSOR c_promolst ( p_emp_id varchar2 ) IS
select pdate
from person.promo
where emp_id = p_emp_id
order by 2 desc;
Begin
for EmpRec in c_emplst ('HIRE')
LOOP
for PromoRec in c_PromoLst ( EmpRec.emp )
LOOP
if PromoRec.Dcode in ('TEMPORARY','RETURN','APPOINTED' )
-- Do all the operation
end if;
end loop;
end loop;
end EMPHIRESEPCHAN;
I have to modify the procedure to retrieve the employee list based on p_Condition1 and p_Condition2 parameters.
If the p_Condition1 is not null and p_Condition2 is null, I have to retrieve the employees who have Rank = 'Developer'
If the p_Condition1 is null and p_Condition2 is not null I have to retrieve the employees who have Rank = 'Tester'
If the p_Condition1 and p_Condition2 is not null I have to retrieve the employees who have Rank both 'Developer' and 'Tester'.
I read so many posts in various sites and found answers which I was not able to follow.
Based on the posts, I made modifications to the cursor as follows
CURSOR c_emplst ( p_listtype varchar2 ) IS
select e.emp_id, e.name, e.Rank
from person.emp e
where ( p_Condition1 = null and p_Condition2 = null = and emp_id in (select distinct(emp_id) from person.promo
where pdate between p_startDate and p_endDate
and dcode in (select adj from support.descr where typ = 'PROMO' and smeaning = p_listtype) )
or ( p_Condition1 > null and p_Condition2 = null = and emp_id in (select distinct(emp_id) from person.promo
where pdate between p_startDate and p_endDate
and Rank ='Developer'
and dcode in (select adj from support.descr where typ = 'PROMO' and smeaning = p_listtype) )
or ( p_Condition1 = null and p_Condition2 > null = and emp_id in (select distinct(emp_id) from person.promo
where pdate between p_startDate and p_endDate
and Rank = 'Tester'
and dcode in (select adj from support.descr where typ = 'PROMO' and smeaning = p_listtype) );
However it's not working.
Thanks for your time and consideration.
I suspect these conditions are your problem:
p_Condition1 = null
Nothing is ever equal to NULL. NULL is not even equal to NULL. Instead, use:
p_Condition1 IS NULL

How do I get column datatype in Oracle with PL-SQL with low privileges?

I have "read only" access to a few tables in an Oracle database. I need to get schema information on some of the columns. I'd like to use something analogous to MS SQL's sp_help.
I see the table I'm interested in listed in this query:
SELECT * FROM ALL_TABLES
When I run this query, Oracle tells me "table not found in schema", and yes the parameters are correct.
SELECT
DBMS_METADATA.GET_DDL('TABLE', 'ITEM_COMMIT_AGG', 'INTAMPS') AS DDL
FROM DUAL;
After using my Oracle universal translator 9000 I've surmised this doesn't work because I don't have sufficient privileges. Given my constraints how can I get the datatype and data length of a column on a table I have read access to with a PL-SQL statement?
ALL_TAB_COLUMNS should be queryable from PL/SQL. DESC is a SQL*Plus command.
SQL> desc all_tab_columns;
Name Null? Type
----------------------------------------- -------- ----------------------------
OWNER NOT NULL VARCHAR2(30)
TABLE_NAME NOT NULL VARCHAR2(30)
COLUMN_NAME NOT NULL VARCHAR2(30)
DATA_TYPE VARCHAR2(106)
DATA_TYPE_MOD VARCHAR2(3)
DATA_TYPE_OWNER VARCHAR2(30)
DATA_LENGTH NOT NULL NUMBER
DATA_PRECISION NUMBER
DATA_SCALE NUMBER
NULLABLE VARCHAR2(1)
COLUMN_ID NUMBER
DEFAULT_LENGTH NUMBER
DATA_DEFAULT LONG
NUM_DISTINCT NUMBER
LOW_VALUE RAW(32)
HIGH_VALUE RAW(32)
DENSITY NUMBER
NUM_NULLS NUMBER
NUM_BUCKETS NUMBER
LAST_ANALYZED DATE
SAMPLE_SIZE NUMBER
CHARACTER_SET_NAME VARCHAR2(44)
CHAR_COL_DECL_LENGTH NUMBER
GLOBAL_STATS VARCHAR2(3)
USER_STATS VARCHAR2(3)
AVG_COL_LEN NUMBER
CHAR_LENGTH NUMBER
CHAR_USED VARCHAR2(1)
V80_FMT_IMAGE VARCHAR2(3)
DATA_UPGRADED VARCHAR2(3)
HISTOGRAM VARCHAR2(15)
You can use the desc command.
desc MY_TABLE
This will give you the column names, whether null is valid, and the datatype (and length if applicable)
The best solution that I've found for such case is
select column_name, data_type||
case
when data_precision is not null and nvl(data_scale,0)>0 then '('||data_precision||','||data_scale||')'
when data_precision is not null and nvl(data_scale,0)=0 then '('||data_precision||')'
when data_precision is null and data_scale is not null then '(*,'||data_scale||')'
when char_length>0 then '('||char_length|| case char_used
when 'B' then ' Byte'
when 'C' then ' Char'
else null
end||')'
end||decode(nullable, 'N', ' NOT NULL')
from user_tab_columns
where table_name = 'TABLE_NAME'
and column_name = 'COLUMN_NAME';
#Aaron Stainback, thank you for correction!
Note: if you are trying to get this information for tables that are in a different SCHEMA use the all_tab_columns view, we have this problem as our Applications use a different SCHEMA for security purposes.
use the following:
EG:
SELECT
data_length
FROM
all_tab_columns
WHERE
upper(table_name) = 'MY_TABLE_NAME' AND upper(column_name) = 'MY_COL_NAME'
select t.data_type
from user_tab_columns t
where t.TABLE_NAME = 'xxx'
and t.COLUMN_NAME='aaa'
Oracle 11.2: Get a list of the full datatype in your table:
create table SOMETABLE (foo integer, bar varchar(300));
select data_type || '(' || data_length || ')' thetype
from user_tab_columns where TABLE_NAME = 'SOMETABLE';
Prints:
NUMBER(22)
VARCHAR(300)
Screenshot:
Documentation:
https://docs.oracle.com/cd/B19306_01/server.102/b14237/statviews_4462.htm#REFRN26277
select column_name, data_type || '(' || data_length || ')' as datatype
from all_tab_columns
where TABLE_NAME = upper('myTableName')
Quick and dirty way (e.g. to see how data is stored in oracle)
SQL> select dump(dummy) dump_dummy, dummy
, dump(10) dump_ten
from dual
DUMP_DUMMY DUMMY DUMP_TEN
---------------- ----- --------------------
Typ=1 Len=1: 88 X Typ=2 Len=2: 193,11
1 row selected.
will show that dummy column in table sys.dual has typ=1 (varchar2), while 10 is Typ=2 (number).
You can try this.
SELECT *
FROM (SELECT column_name,
data_type,
data_type
|| CASE
WHEN data_precision IS NOT NULL
AND NVL (data_scale, 0) > 0
THEN
'(' || data_precision || ',' || data_scale || ')'
WHEN data_precision IS NOT NULL
AND NVL (data_scale, 0) = 0
THEN
'(' || data_precision || ')'
WHEN data_precision IS NULL AND data_scale IS NOT NULL
THEN
'(*,' || data_scale || ')'
WHEN char_length > 0
THEN
'(' || char_length
|| CASE char_used
WHEN 'B' THEN ' Byte'
WHEN 'C' THEN ' Char'
ELSE NULL
END
|| ')'
END
|| DECODE (nullable, 'N', ' NOT NULL')
DataTypeWithLength
FROM user_tab_columns
WHERE table_name = 'CONTRACT')
WHERE DataTypeWithLength = 'CHAR(1 Byte)';
To see the internal representation size in bytes you can use:
REGEXP_SUBSTR(DUMP(your_column_name), 'Len=(\d+)\:', 1, 1, 'c', 1 )
DECLARE
c NUMBER;
d NUMBER;
col_cnt INTEGER;
f BOOLEAN;
rec_tab DBMS_SQL.DESC_TAB;
col_num NUMBER;
PROCEDURE print_rec(rec in DBMS_SQL.DESC_REC) IS
BEGIN
DBMS_OUTPUT.NEW_LINE;
DBMS_OUTPUT.PUT_LINE('col_type = '
|| rec.col_type);
DBMS_OUTPUT.PUT_LINE('col_maxlen = '
|| rec.col_max_len);
DBMS_OUTPUT.PUT_LINE('col_name = '
|| rec.col_name);
DBMS_OUTPUT.PUT_LINE('col_name_len = '
|| rec.col_name_len);
DBMS_OUTPUT.PUT_LINE('col_schema_name = '
|| rec.col_schema_name);
DBMS_OUTPUT.PUT_LINE('col_schema_name_len = '
|| rec.col_schema_name_len);
DBMS_OUTPUT.PUT_LINE('col_precision = '
|| rec.col_precision);
DBMS_OUTPUT.PUT_LINE('col_scale = '
|| rec.col_scale);
DBMS_OUTPUT.PUT('col_null_ok = ');
IF (rec.col_null_ok) THEN
DBMS_OUTPUT.PUT_LINE('true');
ELSE
DBMS_OUTPUT.PUT_LINE('false');
END IF;
END;
BEGIN
c := DBMS_SQL.OPEN_CURSOR;
-- YOUR SELECT HERE
DBMS_SQL.PARSE(c, '
SELECT *
FROM table1 a
bable2 b
table3 c
where a.id = b.id
and b.id2 = c.id
', DBMS_SQL.NATIVE);
d := DBMS_SQL.EXECUTE(c);
DBMS_SQL.DESCRIBE_COLUMNS(c, col_cnt, rec_tab);
col_num := rec_tab.first;
IF (col_num IS NOT NULL) THEN
LOOP
print_rec(rec_tab(col_num));
col_num := rec_tab.next(col_num);
EXIT WHEN (col_num IS NULL);
END LOOP;
END IF;
DBMS_SQL.CLOSE_CURSOR(c);
END;
/

Resources