Oracle Function To Return List - oracle

I'm trying to develop a PLSQL function that outputs a list of employee names that I can then run through another script. I can't quite get it right though. I'm fairly new to PLSQL and am primarily used to building functions in Python, so I may be thinking about this the wrong way. At the end of all of this, I'd like to use the output of this within another script I'm writing.
Baseline Script:
SELECT EMPLOYEE FROM (
SELECT ID, EMPLOYEE, ROLE, STARTDATE,
ROW_NUMBER() OVER (PARTITION BY EMPLOYEE ORDER BY STARTDATE DESC, ID DESC) RN
FROM (
SELECT DISTINCT E.EMPLOYEE EMPLOYEE,
E.ID ID,
LR.DESCRIPTION ROLE,
ROLE_START_DATE STARTDATE
FROM EMPLOYEES E
JOIN ROLES R ON E.EMPLOYEE_ID = R.EMPLOYEE_ID
JOIN LU_ROLES LR ON R.ROLE_ID = LR.ROLE_ID
WHERE ROLE_START_DATE <= DATE '2017-12-03'))
WHERE RN = 1
My attempt at writing a PLSQL function:
CREATE FUNCTION get_employees(EMPLOYEE IN VARCHAR2)
RETURN VARCHAR2
IS EMPLOYEE_LIST;
BEGIN
SELECT EMPLOYEE FROM (
SELECT ID, EMPLOYEE, ROLE, STARTDATE,
ROW_NUMBER() OVER (PARTITION BY EMPLOYEE ORDER BY STARTDATE DESC, ID DESC) RN
FROM (
SELECT DISTINCT E.EMPLOYEE EMPLOYEE,
E.ID ID,
LR.DESCRIPTION ROLE,
ROLE_START_DATE STARTDATE
FROM EMPLOYEES E
JOIN ROLES R ON E.EMPLOYEE_ID = R.EMPLOYEE_ID
JOIN LU_ROLES LR ON R.ROLE_ID = LR.ROLE_ID
WHERE ROLE_START_DATE <= DATE '2017-12-03'))
WHERE RN = 1
RETURN EMPLOYEE_LIST
END;
I know I'm missing some syntax, I just don't know what and why...I'm reading through the docs at the moment to try to understand this. Any help you all could provide would be much appreciated!
Thanks!

Without your tables or sample data this has to be a bit of a guess, but a "fixed" version might be something like this:
create or replace type short_string_tt as table of varchar2(100)
/
create or replace function get_employees
( p_role_date_cutoff roles.role_start_date%type )
return short_string_tt
as
l_employee_list short_string_tt;
begin
select employee bulk collect into l_employee_list
from ( select employee
, row_number() over(partition by employee order by role_start_date desc, id desc) rn
from ( select distinct e.employee, e.id, lr.description, role_start_date
from employees e
join roles r
on r.employee_id = e.id
join lu_roles lr
on lr.role_id = r.role_id
where role_start_date <= p_role_date_cutoff )
)
where rn = 1;
return l_employee_list;
end;
/
If the number of rows returned is likely to be significant then you might look at making it a pipelined function, as these stream rows back as they are fetched rather than building the entire collection in memory before returning anything.

try this would it work ?
CREATE FUNCTION get_employees(EMPLOYEE IN VARCHAR2)
RETURN VARCHAR2
IS EMPLOYEE_LIST VARCHAR2(200);
BEGIN
SELECT EMPLOYEE into EMPLOYEE_LIST FROM (
SELECT ID, EMPLOYEE, ROLE, STARTDATE,
ROW_NUMBER() OVER (PARTITION BY EMPLOYEE ORDER BY STARTDATE DESC, ID DESC) RN
FROM (
SELECT DISTINCT EMPLOYEE EMPLOYEE,
E.ID ID,
LR.DESCRIPTION ROLE,
ROLE_START_DATE STARTDATE
FROM EMPLOYEES E
JOIN ROLES R ON E.EMPLOYEE_ID = R.EMPLOYEE_ID
JOIN LU_ROLES LR ON R.ROLE_ID = LR.ROLE_ID
WHERE ROLE_START_DATE <= DATE '2017-12-03'))
WHERE RN = 1;
RETURN EMPLOYEE_LIST;
END;

Related

Report Job difference in HR schema

I'm new to Oracle and try to practice with HR schema. For example I want to report of those whose job is different from the previous job.
Employee name in employees table and job history in job_history table.
I think the following query will help. (I am considering that current JOB_ID is present in the EMPLOYEES table and you want to compare it with the latest previous JOB_ID from JOB_HISTORY table for the employee)
SELECT E.*, JH.LATEST_PREV_JOB_ID
FROM EMPLOYEES E
JOIN (SELECT FIRST_VALUE(JH.JOB_ID) OVER (PARTITION BY JH.EMPLOYEE_ID
ORDER BY JH.START_DATE DESC NULLS LAST) AS LATEST_PREV_JOB_ID,
JH.EMPLOYEE_ID
FROM JOB_HISTORY JH) JH
ON E.EMPLOYEE_ID = JH.EMPLOYEE_ID
WHERE E.JOB_ID <> JH.LATEST_PREV_JOB_ID
----- Update
You want the query without partition by clause (i.e. without WINDOWS function), We can use the NOT EXISTS as follows:
SELECT E.*, JH.LATEST_PREV_JOB_ID
FROM EMPLOYEES E
JOIN (SELECT JH.JOB_ID AS LATEST_PREV_JOB_ID,
JH.EMPLOYEE_ID
FROM JOB_HISTORY JH
WHERE NOT EXISTS (SELECT 1 FROM JOB_HISTORY JHIN
WHERE JHIN.EMPLOYEE_ID = JH.EMPLOYEE_ID
AND JHIN.START_DATE > JH.START_DATE)) JH
ON E.EMPLOYEE_ID = JH.EMPLOYEE_ID
WHERE E.JOB_ID <> JH.LATEST_PREV_JOB_ID
If I understood right your question, then the answer maybe something like this:
select
e.first_name,
e.last_name,
e.job_id as prev_job,
jh.job_id as last_lob
from
employees e,
job_history jh,
(select
employee_id,
max(end_date) as max_end_date
from
job_history
group by
employee_id
) t
where
(jh.employee_id = e.employee_id) and
(jh.job_id <> e.job_id) and
(jh.end_date = t.max_end_date) and
(t.employee_id = jh.employee_id)

Leetcode Oracle - ORA-00923: FROM keyword not found where expected

I am trying to solve this problem using Oracle SQL but I keep getting this error -
ORA-00923: FROM keyword not found where expected
link to problem - https://leetcode.com/problems/department-top-three-salaries/submissions/
my solution until now - just to query the data -
with temp as
(
select d.Name as Department,
e.Name as Employee,
e.Salary as Salary
from employee e
join department d
on e.DepartmentId = d.Id
)
select *
, rank() over (partition by department order by salary desc) as rr
from temp
but if i simply run this then it works fine -
with temp as
(
select d.Name as Department,
e.Name as Employee,
e.Salary as Salary
from employee e
join department d
on e.DepartmentId = d.Id
)
select *
from temp
And if I run this then it runs okay -
select department, employee, salary
from
(
select A.* , dense_rank() over (partition by department order by salary desc) as rr
from
(
select d.Name as Department,
e.Name as Employee,
e.Salary as Salary
from employee e
join department d
on e.DepartmentId = d.Id
) A
) B
where rr <= 3
does it mean i cannot use the cascading with statements in oracle ?
For instance, cant I write -
with temp as
(
select col1, col2 from table
)
, temp1 as
(
select *, "hello" as col3
from temp
)
select *
from temp1
in Oracle?
If you need to select only all the columns then * without the alias is fine. But if You need to give alias of the table wherever you want to select all the columns of the table using * and also another expression in SELECT clause.
with TEMP AS
( SELECT
COL1,
COL2
FROM table )
, TEMP1 AS
(SELECT
T.*, -- alias here
"hello" AS COL3
FROM TEMP T
)
select * FROM TEMP1;
with temp as
(
select d.Name as Department,
e.Name as Employee,
e.Salary as Salary
from employee e
join department d
on e.DepartmentId = d.Id
)
select T.* -- alias here
, rank() over (partition by department order by salary desc) as rr
from temp T;

Oracle Left join give duplicate

I am trying to get all the customers whether they have install service or not from TRANS_TABLE.
NOA- query to get only the MAX product and join again with TRANS_TABLE by email id to get the all the MAX customers details (wwhther they have install service by adding Y OR N, but this query return duplicate with REP Product as well
Below is my Oracel Query which give duplicated
with CTE as (SELECT NOA.*,
CASE
WHEN TRANS_TABLE.product_name LIKE '%Installation%' THEN 'Y'
ELSE 'N'
END AS Installaion ,
ROW_NUMBER() OVER (PARTITION BY TRANS_TABLE.email_address ORDER BY TRANS_TABLE.email_address) AS rn
FROM (SELECT DISTINCT email_address
FROM TRANS_TABLE
WHERE email_address IS NOT NULL
and pdct_name like '%MAX%'
) NOA
LEFT JOIN TRANS_TABLE
ON NOA.email_address = TRANS_TABLE.email_address
select * from cte where rn='1'
The following code will help:
CTE AS (
SELECT
NOA.*,
CASE
WHEN TRANS_TABLE.PDCT_NAME LIKE '%INSTALLATION%' THEN 'Y' -- case sensitive name is used
ELSE 'N'
END AS INSTALLAION,
ROW_NUMBER() OVER(
PARTITION BY TRANS_TABLE.EMAIL_ADDRESS
ORDER BY
TRANS_TABLE.EMAIL_ADDRESS
) AS RN
FROM
(
SELECT DISTINCT
PDCT_NAME,
EMAIL_ADDRESS
FROM
TRANS_TABLE
WHERE
EMAIL_ADDRESS IS NOT NULL
AND PDCT_NAME LIKE '%MAX%'
) NOA
LEFT JOIN TRANS_TABLE ON NOA.EMAIL_ADDRESS = TRANS_TABLE.EMAIL_ADDRESS
WHERE TRANS_TABLE.PDCT_NAME NOT LIKE '%REP%') -- added this WHERE condition
SELECT
PDCT_NAME, EMAIL_ADDRESS, INSTALLAION
FROM
CTE
WHERE
RN = '1'
db<>fiddle demo
Cheers!!

Oracle select random rows matching a join condition

My objective is simple, I have to create a temporary table with some random values from a employee table whenever the department is in some particular department (say 2). For the rest of departments I don't care the value, it can be NULL.
Currently I have the following :
create table test
as
select s.DEPTNAME,
cast (
(case when s.DEPTID in (2) then
(SELECT a.ENAME FROM
(SELECT b.ENAME, b.DEPTID FROM EMPLOYEE b
WHERE b.DEPTID IS NOT NULL
ORDER BY DBMS_RANDOM.VALUE) a
WHERE a.DEPTID = s.DEPTID AND ROWNUM = 1
)
END)
AS VARCHAR2(30)) "ENAME" from DEPARTMENT s;
But the main issue here is related to performance. For every department value in 2 we do a sort of EMPLOYEE table to get a single random ENAME.
Is there a better way to do this ? I know sample might work but I want to achieve more randomness.
First idea - join randomly numbered enames:
with
e as (select ename, deptid, row_number() over (order by dbms_random.value) rn
from employee where deptid = 2),
c as (select count(1) cnt from e),
d as (select deptname, deptid, round(dbms_random.value(1, c.cnt)) rn from department, c)
select d.deptname, e.ename from d left join e using (rn, deptid)
SQLFiddle demo
Second possible solution, which worked for me, is to create function returning random ename from table employee
and use it in your query, but it would be probably slower.
Edit - according to comment:
If, for some reason, the first part of your statement is "fixed", then you could use this syntax:
create table test as
select deptname, ename from (
with
e as (select ename, deptid, row_number() over (order by dbms_random.value) rn
from employee where deptid = 2),
c as (select count(1) cnt from e),
d as (select deptname, deptid, round(dbms_random.value(1, c.cnt)) rn
from department cross join c)
select d.deptname, e.ename from d left join e using (rn, deptid));

Oracle PL/SQL group by issue

I want to do this excercise for Oracle 10g Express
"Write an SQL query to retrieve the department name, firstname,
lastname, salary of the employee who earns the maximum salary for that
department."
I tried this code but it is not working for me.
There are two tables named employees, departments
ERROR is: ORA-00979: not a GROUP BY expression
SELECT first_name, last_name, departments.department_name, salary
FROM employees, departments
where employees.department_id = departments.department_id
group by salary
Output must be like that.
You will want to use an aggregate function to get the result. In this case you will use max() to get the highest salary for each department.
There are several ways that this can be written.
You can use a subquery in a join:
select d.department_name,
e.first_name,
e.last_name,
e.salary
from employees e
inner join
(
select max(salary) MaxSalary, department_id
from employees
group by department_id
) e1
on e.department_id = e1.department_id
and e.salary = e1.maxsalary
inner join departments d
on e.department_id = d.department_id;
Since you are using Oracle, you can use windowing functions to get the result:
select department_name, first_name, last_name, salary
from
(
select d.department_name,
e.first_name,
e.last_name,
e.salary,
row_number() over(partition by d.department_id order by e.salary desc) rn
from employees e
inner join departments d
on e.department_id = d.department_id
) d
where rn = 1
Or you can even use a WHERE clause to filter the data:
select d.department_name,
e.first_name,
e.last_name,
e.salary
from employees e
inner join departments d
on e.department_id = d.department_id
where salary in (select max(salary)
from employees e1
where e.department_id = e1.department_id
group by department_id)
I understand the question that you are asking is in reference to using GROUP BY but I would take a closer look at the question.
"Write an SQL query to retrieve the department name, firstname, lastname, salary of the employee who earns the maximum salary for that department."
I think you would also benefit from looking at the Maximum function since you are looking for the employee with the Maximum salary.
You need an aggregate function to be able to use GROUP BY. Aggregate functions are functions that perform a task over several records. These are functions like SUM, AVG, and COUNT. When you have an aggregate function, you group by whatever is not in the function.
In your example, MAX would be your aggregate function and you can use your GROUP BY successfully.
Here is a link for Aggregate Functions

Resources