I want to Create a function that returns the number of employees in a department - sql

The query I tried is this,
CREATE OR REPLACE FUNCTION get_dno (d_no IN Employee.DepartmentNo%TYPE)
RETURN NUMBER
IS
d_cnt NUMBER;
BEGIN
SELECT COUNT(*) INTO d_cnt
FROM employee GROUP BY DeptNo=d_no;
RETURN d_cnt;
END get_dno;
and use this query below to get value from user.
DECLARE
get_dptno NUMBER;
BEGIN
get_dptno := get_dno(:d_no);
DBMS_OUTPUT.PUT_LINE('No of employees in searched DeptNo : ' ||get_dptno);
END;
But I couldn't even get the Function part to run.
The error I got was where I used GROUP BY, I think
This is the error message I got
Is there any workaround for the same logic ???

When you group by column there is no condition accept please remove code after column name DeptNo.
CREATE OR REPLACE FUNCTION get_dno (d_no IN Employee.DepartmentNo%TYPE)
RETURN NUMBER
IS
d_cnt NUMBER;
BEGIN
SELECT COUNT(*) INTO d_cnt
FROM employee GROUP BY DeptNo;
RETURN d_cnt;
END get_dno;

You are almost there, you want WHERE rather than GROUP BY and you need to work out if the column name is DepartmentNo or DeptNo and use the correct column name in the signature and in the SELECT query:
CREATE FUNCTION get_dno (
d_no IN Employee.DepartmentNo%TYPE
) RETURN NUMBER
IS
d_cnt NUMBER;
BEGIN
SELECT COUNT(*)
INTO d_cnt
FROM employee
WHERE DepartmentNo=d_no;
RETURN d_cnt;
END get_dno;
/
Then:
DECLARE
get_dptno NUMBER;
BEGIN
get_dptno := get_dno(:d_no);
DBMS_OUTPUT.PUT_LINE('No of employees in searched DeptNo : ' ||get_dptno);
END;
/
Works and, for the sample data:
CREATE TABLE employee (id, departmentno) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 1 FROM DUAL UNION ALL
SELECT 3, 1 FROM DUAL UNION ALL
SELECT 4, 1 FROM DUAL UNION ALL
SELECT 5, 2 FROM DUAL UNION ALL
SELECT 6, 2 FROM DUAL UNION ALL
SELECT 7, 3 FROM DUAL UNION ALL
SELECT 8, 3 FROM DUAL UNION ALL
SELECT 9, 3 FROM DUAL UNION ALL
SELECT 10, 4 FROM DUAL;
If :dno is 1 then outputs:
No of employees in searched DeptNo : 4
db<>fiddle here

Related

i get ORA-01722: invalid number error in plsql

i have problem about ora 01722 i have string prols it is like "1501,1701,1901,2001,2501,2601" or "2321,1331,111231,35501" and i want control table x' column x.rol_id include prols or not but i get error in my code. here is part of my code. thanks.
function get_menu_detay_01(pmenu_kod varchar2,
proller varchar2) return sys_refcursor is
v_cr_1 sys_refcursor;
begin
open v_cr_1 for
select distinct mtd.sira_no,
mtd.seviye_1,
mtd.fa_icon_1,
mtd.seviye_2,
mtd.fa_icon_2,
mtd.seviye_3,
mtd.fa_icon_3,
ut.uygulama_tanim_id,
ut.aciklama,
ut.path,
ut.fa_icon,
ut.href,
ut.onclick
from uat.menu_tanim_d mtd
left join uat.uygulama_tanim ut
on ut.uygulama_tanim_id = mtd.uygulama_tanim_id
left join uat.uygulama_yetki uy
on mtd.uygulama_tanim_id = uy.uygulama_tanim_id
where mtd.menu_kod = pmenu_kod
and uy.rol_id in (select regexp_substr(tt.rol, '[^,]+', 1, level)
from (select proller rol
from dual t) tt
connect by regexp_substr(tt.rol, '[^,]+', 1, level) is not null)
order by mtd.sira_no;
return v_cr_1;
end;
If you say the strings you are splitting has separator , then you need to change the regular expression [^,]+ to to include , instead ; or the other way around
I just tried and its working,
WITH tst
AS
(SELECT 1501 rol_id FROM dual
UNION ALL
SELECT 1701 FROM dual
UNION ALL
SELECT 1901 FROM dual
UNION ALL
SELECT 2001 FROM dual
UNION ALL
SELECT 2501 FROM dual
UNION ALL
SELECT 2601 FROM dual
)
SELECT *
FROM tst x
WHERE x.rol_id in (select regexp_substr(tt.rol, '[^,]+', 1, LEVEL)
from (select '1701,1901,2001,2501,2601' rol
from dual t) tt
connect by regexp_substr(tt.rol, '[^,]+', 1, level) is not null);
EDIT: Test with function Please check how you call the function and pass the string in proper format or not
CREATE OR REPLACE FUNCTION get_menu_detay_01(proller VARCHAR2)
RETURN SYS_REFCURSOR
IS
v_cr_1 SYS_REFCURSOR;
BEGIN
OPEN v_cr_1 FOR
SELECT *
FROM ( SELECT 1501 rol_id FROM dual
UNION ALL
SELECT 1701 FROM dual
UNION ALL
SELECT 1901 FROM dual
UNION ALL
SELECT 2001 FROM dual
UNION ALL
SELECT 2501 FROM dual
UNION ALL
SELECT 2601 FROM dual
) x
WHERE x.rol_id IN (SELECT regexp_substr(tt.rol,'[^,)]+',1,LEVEL)
FROM (SELECT proller rol FROM dual t) tt
CONNECT BY regexp_substr(tt.rol,'[^,]+',1,LEVEL) IS NOT NULL);
RETURN v_cr_1;
END get_menu_detay_01;
Test:
set serveroutput on;
DECLARE
lo_ref_cur SYS_REFCURSOR;
lo_number NUMBER;
BEGIN
lo_ref_cur := get_menu_detay_01('1701,1901,2001,2501,2601');
LOOP
FETCH lo_ref_cur INTO lo_number;
EXIT WHEN lo_ref_cur%NOTFOUND;
dbms_output.put_line(lo_number);
END LOOP;
END;
/
Output:
------
1701
1901
2001
2501
2601
PL/SQL procedure successfully completed.

Return Multiple Values from Oracle Function

I want to create a function that returns multiple rows into a table that is of object type.
I have created an object and a nested table object and now when I run the function there is an error which says
PL/SQL: SQL Statement ignored
PL/SQL: ORA-00947: not enough values
-- Object type creation
create or replace type test_object_sn as object
(
column_1 varchar2(30),
column_2 varchar2(30),
column_3 number
);
-- Table of object
create or replace type test_otable_sn as table of test_object_sn;
-- function (where I get an error)
create or replace function load_test_object_sn
return test_otable_sn
as
details test_otable_sn;
begin
with ad as (select 'a', 'b', 4 from dual
union all
select 'r', '5', 3 from dual
union all
select 'g', 's', 3 from dual)
select * into details from ad;
return details;
end;
I want to have the test_otable_sn table object loaded with the data and then query it using the table() function via my load_test_object_sn function
e.g. select * from table(load_test_object_sn);
Update:
do you know how to modify this for scenario whereby I have an sql
statement contained in a string variable to execute?
Yes, we can use a cursor reference (SYS_REFCURSOR) and OPEN/FETCH/CLOSE instead of a CURSOR and CURSOR FOR LOOP.
The syntax is OPEN <cursor-reference> FOR <string-containing-sql-statement> . See below.
CREATE OR REPLACE FUNCTION load_test_object_sn
RETURN test_otable_sn
AS
details test_otable_sn := test_otable_sn();
-- Variable stores SQL statement for cursor
l_sql CLOB :=
q'[with ad as (
select 'a' column_1, 'b' column_2, 4 column_3 from dual union all
select 'r', '5', 3 from dual union all
select 'g', 's', 3 from dual
)
select *
from ad]';
-- Cursor reference allows us to open cursor for SQL statement above
rc SYS_REFCURSOR;
-- Define object instance to store each row fetched from the cursor
l_obj test_object_sn := test_object_sn(NULL, NULL, NULL);
i PLS_INTEGER := 1;
BEGIN
-- Explicitly open, fetch from, and close the cursor
OPEN rc FOR l_sql;
LOOP
FETCH rc INTO l_obj.column_1, l_obj.column_2, l_obj.column_3;
EXIT WHEN rc%NOTFOUND;
details.extend();
details(i) := test_object_sn(l_obj.column_1, l_obj.column_2, l_obj.column_3);
i := i + 1;
END LOOP;
CLOSE rc;
RETURN details;
END;
Original answer:
Unfortunately, one can't use SELECT * INTO with a collection in this manner, so here's an alternative way to populate the table:
create or replace function load_test_object_sn
return test_otable_sn
as
details test_otable_sn := test_otable_sn();
cursor c_ad is
with ad as (select 'a' column_1, 'b' column_2, 4 column_3 from dual
union all
select 'r', '5', 3 from dual
union all
select 'g', 's', 3 from dual)
select * from ad;
i pls_integer := 1;
begin
for ad_rec in c_ad loop
details.extend();
details(i) := test_object_sn(ad_rec.column_1, ad_rec.column_2, ad_rec.column_3);
i := i + 1;
end loop;
return details;
end;
/
Output:
SQL> SELECT * FROM TABLE(load_test_object_sn);
COLUMN_1 COLUMN_2 COLUMN_3
---------- ---------- ----------
a b 4
r 5 3
g s 3

How can I pass comma separated value in cursor's select statement where clause

I have following Block which has a cursor and a select query. I want to pass the output of select, which is comma separated into
the cursor's select statement, into the where clause.
I know below code will throw an error because of SQL query in Declare section but how I achieve this using array or collection.
here, id column is number
code snippet:
declare
test varchar2(30);
SELECT LISTAGG(value, ', ') WITHIN GROUP (ORDER BY value2) into test from table3 where value2=12;
cursor c1 (select * from table where id in (test))
begin
for i in c1 loop
null;
end loop;
end;
Why would you ever do this?
You can simple write you select as:
Select * from table where id in (select value from table3 where value2=12)
Edit:
Also you need to open your cursor c1, for it to work AFAIK.
You can use a collection and the MEMBER OF operator:
Oracle Setup:
CREATE TYPE IntegerList AS TABLE OF NUMBER(8,0);
/
CREATE TABLE table1 ( value, value2 ) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 3, 4 FROM DUAL UNION ALL
SELECT 5, 3 FROM DUAL UNION ALL
SELECT 7, 2 FROM DUAL;
CREATE TABLE table2 ( id, value ) AS
SELECT 1, 11 FROM DUAL UNION ALL
SELECT 2, 22 FROM DUAL UNION ALL
SELECT 3, 33 FROM DUAL UNION ALL
SELECT 7, 77 FROM DUAL;
PL/SQL:
DECLARE
test IntegerList;
c1 SYS_REFCURSOR;
BEGIN
SELECT value
BULK COLLECT INTO test
FROM table1;
FOR r IN ( SELECT * FROM table2 WHERE id MEMBER OF test ) LOOP
DBMS_OUTPUT.PUT_LINE( r.id || ', ' || r.value );
END LOOP;
END;
/
Output:
1, 11
3, 33
7, 77
db<>fiddle here

Using Oracle SQL, how can I get weekdays (Monday-Sunday) using their rank (1-7)?

Using Oracle, here is my problem :
What I have:
A number between 1 and 7.
What I want:
1 = Monday, 2 = Tuesday...
What I did so far:
DECODE(myVar,1,'Monday',2,'Tuesday',...)
I have of course thought of writing a function but I would like to know if there is no native way to do that.
EDIT : Since it seems unclear, I want a native way to do this without using a DECODE or a CASE.
Thanks a lot.
You may try to use to_char function with the Day and D arguments for a date variable as in the following :
SQL> var day_nr number;
SQL> exec :day_nr := 1;
PL/SQL procedure successfully completed
day_nr
---------
1
SQL> alter session set NLS_TERRITORY="UNITED KINGDOM";
SQL> with t(day,day_nr) as
2 (
3 select to_char(level + sysdate,'Day','NLS_DATE_LANGUAGE=ENGLISH'),
4 to_char(level + sysdate,'D')
5 from dual
6 connect by level <= 7
7 )
8 select day
9 from t
10 where day_nr = :day_nr;
DAY
---------
Monday
As an example, if you substitute 1 for :day_nr you get Monday etc.
Rextester Demo
P.S. thanks to #Matthew McPeak I realize that the returning value for to_char(<date>,'D') may differ from TERRITORY, for example, it differs whether NLS_TERRITORY parameter is set "UNITED KINGDOM" or "AMERICAN_AMERICA".
The following formula will produce what you want:
SELECT TO_CHAR(TRUNC(SYSDATE, 'IW') +
(CAST(TO_CHAR(TRUNC(SYSDATE, 'IW'), 'D') AS NUMBER) +
:DAY_OF_WEEK - 3), 'Day')
FROM DUAL
To use, replace the :DAY_OF_WEEK parameter with whatever value you want and it will return the day of the week.
TO_DATE has a format_mask that allows you to create a date from the numeric day of the week. Then you could get the weekday name from that. However it requires a string input.
this will surely work as you have asked a function without decode or case:
declare
a varchar(20);
b varchar(20);
c varchar(20);
d varchar(20);
e varchar(20);
f varchar(20);
g varchar(20);
h varchar(20);
i number;
begin
i:=&i;
SELECT COALESCE((select 'null' from dual where i<>1),'MONDAY')into a from dual;
SELECT COALESCE((select 'null' from dual where i<>2),'TUEDAY')into b from dual;
SELECT COALESCE((select 'null' from dual where i<>3),'WEDNESDAY') into c from dual;
SELECT COALESCE((select 'null' from dual where i<>4),'THURDAY') into d from dual;
SELECT COALESCE((select 'null' from dual where i<>5),'FRIDAY')into e from dual;
SELECT COALESCE((select 'null' from dual where i<>6),'SATURDAY')into f from dual;
SELECT COALESCE((select 'null' from dual where i<>7),'SUNDAY')into g from dual;
DBMS_OUTPUT.PUT_LINE(a||b||c||d||e||f||g);
end;
/
for input of 4 output is:
null null null THURDAY null null null

Oracle Package return table

I am trying to return a table via a function in an Oracle package:
CREATE OR REPLACE PACKAGE test AS
TYPE rec IS RECORD(
col1 VARCHAR(10));
TYPE rec_table IS TABLE OF rec;
FUNCTION get_table(input VARCHAR2)
RETURN rec_table
PIPELINED;
END;
CREATE OR REPLACE PACKAGE BODY test AS
FUNCTION get_table(input VARCHAR2)
RETURN rec_table
PIPELINED IS
rec1 rec;
BEGIN
SELECT * INTO rec1
FROM
(
SELECT '1' from dual
UNION ALL
SELECT '2' from dual
);
PIPE ROW (rec1)
RETURN;
END get_table;
END;
but when I try to run
select * from table(test.get_table('blah'))
I get an error: exact fetch returns more then requested number of rows
I've read a bit about BULK COLLECT INTO, but I am not understanding the syntax...
The following piece of code:
SELECT '1' from dual
UNION ALL
SELECT '2' from dual
Returns two, not one record, and you are trying to put those two records in one rec variable. You should instead loop through the results of the UNION:
FOR v_rec IN (
SELECT *
FROM (
SELECT '1' from dual
UNION ALL
SELECT '2' from dual
)
)
LOOP
PIPE ROW (v_rec);
END LOOP;