Explicit type PostgreSQL - sql

I have a problem. There is a function, that counts total budget of department, including departments lower down the hierarchy:
CREATE OR REPLACE FUNCTION PUBLIC.DEPT_BUDGET (DNO BPCHAR(3))
RETURNS TABLE (
TOT DECIMAL(12,2)
)
AS $DEPT_BUDGET$
DECLARE sumb DECIMAL(12, 2);
DECLARE rdno BPCHAR(3)[];
DECLARE cnt INTEGER;
DECLARE I BPCHAR(3);
BEGIN
tot = 0;
SELECT "BUDGET" FROM department WHERE dept_no = dno INTO tot;
SELECT count("BUDGET") FROM department WHERE head_dept = dno INTO cnt;
IF cnt = 0
THEN RETURN QUERY SELECT "BUDGET" FROM department WHERE dept_no = dno;
END IF;
SELECT
ARRAY_AGG(dept_no)
FROM
department
WHERE
head_dept = dno
INTO
rdno;
FOREACH I IN ARRAY rdno
LOOP
SELECT * FROM DEPT_BUDGET(I) INTO SUMB;
tot = tot + sumb;
END LOOP;
END; $DEPT_BUDGET$ LANGUAGE plpgsql;
The dept_no has bpchar(3) type. When I'm trying to call a function SELECT public.dept_budget('000'::VARCHAR); , I got an error:
SQL Error [42883]: ERROR: function dept_budget(integer) does not exist. No function matches the given name and argument types. You might need to add explicit type casts.
When I change in-type parameter on bpchar or char, I got another error:
SQL Error [22004]: ERROR: FOREACH expression must not be null.
I don't understand, why forced typization doesn't work. What should I do?
Types of data
UPD: Yeah, there is bpchar, but I have already tried to change everywhere VARCHAR(3) on BPCHAR(3), and there is still an error.

This function could be written in sql instead of plpgsql. Try this :
CREATE OR REPLACE FUNCTION PUBLIC.DEPT_BUDGET (DNO VARCHAR)
RETURNS DECIMAL(12,2) LANGUAGE sql AS $$
SELECT sum( CASE
WHEN dept_no = dno
THEN budget
ELSE dept_budget(dept_no)
END
) :: decimal(12,2)
FROM department
WHERE dept_no = dno
OR head_dept = dno ;
$$ ;

Related

Creating Custom columns in stored procedure postgres

I am using a stored procedure to return the type of student that is enrolled at my college. Pushing their ID through should return their first name and last name in a new column that is going to be made(Ex: commuter, employee, resident). I keep getting an error:
ERROR: syntax error at or near "if"
LINE 8: if exists (select count(commuterid) > 0 from commuter wh...).
Any tips or ideas?
create or replace function roleAtMarist(int, REFCURSOR) returns refcursor as
$$
declare
identifier int := $1;
resultset refcursor := $2;
begin
open resultset for
if exists (select count(commuterid) > 0 from commuter where commuterid = identifier) then
select fname, lname, "Commuter" as Role
from people
where peopleid = identifier;
end if;
if exists (select count(employeeid) > 0 from employee where emplpoyeeid = identifier) then
select fname, lname, "Employee" as Role
from people
where peopleid = identifier;
end if;
if exists (select count(residentid) > 0 from studentpark where residentid = identifier) then
select fname, lname, "Resident" as Role
from people
where peopleid = identifier;
end if;
return resultset;
end;
$$
language plpgsql;
select roleAtMarist(12, 'resultset') ;
fetch all from results ;
This is backwards in multiple ways. A cursor would use valid SQL and no plpgsql commands. But you don't need a cursor nor plpgsql to begin with. A simple SQL function should do:
CREATE OR REPLACE FUNCTION role_at_marist(_identifier int)
RETURNS TABLE (fname text, lname text, "role" text) AS
$func$
SELECT p.fname, p.lname, text 'Commuter'
FROM people
WHERE p.peopleid = $1
AND EXISTS (SELECT 1 FROM commuter c WHERE c.commuterid = p.peopleid)
UNION ALL
SELECT p.fname, p.lname, 'Employee'
FROM people
WHERE p.peopleid = $1
AND EXISTS (SELECT 1 FROM employee e WHERE e.emplpoyeeid = p.peopleid)
UNION ALL
SELECT p.fname, p.lname, 'Resident'
FROM people
WHERE p.peopleid = $1
AND EXISTS (SELECT 1 FROM studentpark s WHERE s.residentid = p.peopleid)
$func$ LANGUAGE sql;
Call:
SELECT * FROM role_at_marist(12);
Set-returning functions can be used just like tables in the FROM list.
String literals are enclosed in single quotes!

pl/sql function- pass multiple varchar2 values dynamically for SELECT ... NOT IN()

I have a table EMP with following definition:
EMP_ID NOT NULL NUMBER(6)
EMP_NAME NOT NULL VARCHAR2(25)
EMAIL NOT NULL VARCHAR2(25)
PHONE_NUMBER VARCHAR2(20)
HIRE_DATE NOT NULL DATE
JOB_ID NOT NULL VARCHAR2(10)
SALARY NUMBER(8,2)
If we want to count employees whose name not in 'King', 'Steve', 'John' we simply use this query :
SELECT count(*) FROM emp WHERE emp_name NOT IN('King','Steve','John');
Now Here is what I want above this:
What I want to do is, Create a PL/SQL Function which returns number of count according to the dynamic input, Like if we pass:
SELECT count_emp('King,Steve,John') FROM dual;
SELECT count_emp('William,Donald') FROM dual;
SELECT count_emp('Daniel') FROM dual;
needs to return appropriate count, how can I achieve this using PL/SQL FUNCTION
This is what I have tried and Needs guideline:
CREATE OR REPLACE FUNCTION count_emp(emp_nm IN varchar)
RETURN number
IS
cnt NUMBER;
BEGIN
SELECT count(*) INTO cnt FROM emp WHERE emp_name NOT IN(emp_nm);
RETURN cnt;
END;
it is giving result for single name, but how can I split/format multiple input(i.e. emp_nm) to pass in NOT IN()?
Try like this,
CREATE OR REPLACE
FUNCTION count_emp(emp_nm IN VARCHAR)
RETURN NUMBER
IS
cnt NUMBER;
BEGIN
SELECT count(*)
INTO cnt
FROM emp
WHERE ename NOT IN(
SELECT regexp_substr (emp_nm, '[^,]+', 1, ROWNUM)
FROM dual
CONNECT BY LEVEL <= LENGTH (regexp_replace (emp_nm, '[^,]+')) + 1);
RETURN cnt;
END;
You can try dynamic sql:
CREATE OR REPLACE FUNCTION count_emp(emp_nm IN varchar)
RETURN number
IS
cnt NUMBER;
BEGIN
Execute immediate 'SELECT count(*) FROM emp WHERE emp_name NOT IN(' || emp_nm || ')' returning into cnt;
RETURN cnt;
END;
You can also use MEMBER OF. Here is a snippet. Hope this helps!
-- Create a type in SQL
CREATE OR REPLACE TYPE t_emp_name AS TABLE OF VARCHAR2 (10);
-- use MEMBER OF to use your list as IN parameter
CREATE OR REPLACE FUNCTION count_emp (emp_nm IN t_emp_name)
RETURN NUMBER
IS
cnt NUMBER;
BEGIN
SELECT COUNT (*)
INTO cnt
FROM emp
WHERE emp_name NOT MEMBER OF (emp_nm);
RETURN cnt;
END;
-- Assign values to the list, you can do it dynamically as well. Call the function
DECLARE
l_emp_name_list t_emp_name;
lv_count NUMBER;
BEGIN
l_emp_name_list := t_emp_name ('King', 'Steve'); --add more names as needed
lv_count := count_emp (l_emp_name_list);
END;

Oracle SQL Code

Trying to use this code, which was provided on here, however, it returns the following error when trying to run:
PLS-00103: Encountered the symbol "IS" when expecting one of the following:
constant exception table long double ref char time timestamp interval
date binary national character nchar
1. DECLARE
2. emp employee%ROWTYPE;
3. tbl_emp IS TABLE OF emp;
4. v_user_type employee.user_type%TYPE;
5. BEGIN
Code is:
DECLARE
emp employee%ROWTYPE;
tbl_emp IS TABLE OF emp;
v_user_type employee.user_type%TYPE;
BEGIN
SELECT user_type
INTO v_user_type
FROM Employee
WHERE upper(username) = v('APP_USER');
IF v_user_type = 1
THEN
SELECT * BULK COLLECT INTO tbl_emp
FROM employee;
ELSE
SELECT * BULK COLLECT INTO tbl_emp
FROM employee;
WHERE upper(username) = v('APP_USER');
END IF;
END;
/
You need to first declare type, then variable of that type.
E.g.
declare
type tbl_emp_type IS TABLE OF employee%ROWTYPE;
tbl_emp tbl_emp_type;
begin
select *
bulk collect into tbl_emp
from employee;
end;
SQL Fiddle

Oracle: Return multiple values in a function

I'm trying to return a multiple values in a %rowtype from a function using two table(employees and departments), but it not working for me.
create or replace function get_employee
(loc in number)
return mv_emp%rowtype
as
emp_record mv_emp%rowtype;
begin
select a.first_name, a.last_name, b.department_name into emp_record
from employees a, departments b
where a.department_id=b.department_id and location_id=loc;
return(emp_record);
end;
The above function compiled without any error? What is the type of MV_EMP? Ideally, it should be something like below.
create or replace type emp_type
(
first_name varchar2(20)
, last_name varchar2(20)
, depart_name varchar2(20)
)
/
create or replace function get_employee
(loc in number)
return emp_type
as
emp_record emp_type;
begin
select a.first_name, a.last_name, b.department_name into emp_record
from employees a, departments b
where a.department_id=b.department_id and location_id=loc;
return(emp_record);
end;
create type t_row as object (a varchar2(10));
create type t_row_tab as table of t_row;
We will now create a function which will split the input string.
create or replace function get_number(pv_no_list in varchar2) return t_row_tab is
lv_no_list t_row_tab := t_row_tab();
begin
for i in (SELECT distinct REGEXP_SUBSTR(pv_no_list, '[^,]+', 1, LEVEL) no_list FROM dual
CONNECT BY REGEXP_SUBSTR(pv_no_list, '[^,]+', 1, LEVEL) IS NOT NULL)
loop
lv_no_list.extend;
lv_no_list(lv_no_list.last) := t_row(i.no_list);
end loop;
return lv_no_list;
end get_number;
Once the function is in place we can use the table clause of sql statement to get the desired result. As desired we got multiple values returned from the function.
SQL> select * from table(get_number('1,2,3,4'));
A
----------
1
3
2
4
So now our function is simply behaving like a table. There can be a situation where you want these comma separated values to be a part of "IN" clause.
For example :
select * from dummy_table where dummy_column in ('1,2,3,4');
But the above query will not work as '1,2,3,4' is a string and not individual numbers. To solve this problem you can simply use following query.
select * from dummy_table where dummy_column in ( select * from table(get_number('1,2,3,4')) );
References : http://www.oraclebin.com/2012/12/returning-multiple-values-from-function.html
CREATE OR replace FUNCTION Funmultiple(deptno_in IN NUMBER)
RETURN NUMBER AS v_refcursur SYS_REFCURSOR;
BEGIN
OPEN v_refcursor FOR
SELECT *
FROM emp
WHERE deptno = deptno_in;
retun v_refcursor;
END;
To call it, use:
variable x number
exec :x := FunMultiple(10);
print x

Use table of number as filter in where clause

I want to use a Table of Number as a filter in a select statement.
This is the Table of Numbers:
CREATE OR REPLACE TYPE AUTOCONTROLE2.ListNumbers AS TABLE OF NUMBER(10)
This is a little test sql:
declare
testvar number;
ActiviteitSAC autocontrole2.ListNumbers := autocontrole2.ListNumbers(189449, 189456, 189473);
begin
select count(O.pap_id) into testvar
from pap_operator O
where O.PAP_OPERATOR_ID in (ActiviteitSAC(1), ActiviteitSAC(2), ActiviteitSAC(3));
end;
I want to replace the ActiviteitSAC(1), ActiviteitSAC(2), ActiviteitSAC(3) by something like
"select * from ActiviteitSAC".
Any ideas?
SELECT count( o.pap_id)
INTO testvar
FROM pap_operator o
WHERE o.pap_operator_id IN (SELECT * FROM TABLE(ActiveiteitSAC) );
should do it.