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;
Related
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)
How can I convert the below query in Oracle to Hive?
SELECT A.EMP_NO, A.LOGIN_TIMESTAMP FROM TABLE1 A, TABLE2 B
WHERE A.EMP_NO = 1234 AND B.EMP_CURR =
(SELECT MIN(EMP_CURR) FROM TABLE2 WHERE EMP_NO = A.EMP_NO AND
LOGIN_TIMESTAMP = A.LOGIN_TIMESTAMP AND EMP_STATUS_CODE <> 'P')
Use dense_rank() to get rows with minimum EMP_CURR:
SELECT A.EMP_NO, A.LOGIN_TIMESTAMP
FROM TABLE1 A
INNER JOIN (select B.*,
dense_rank() over(partition by B.EMP_NO, B.LOGIN_TIMESTAMP order by B.EMP_CURR) rn
from TABLE2 B where EMP_STATUS_CODE <> 'P'
) B
on B.EMP_NO = A.EMP_NO and B.LOGIN_TIMESTAMP = A.LOGIN_TIMESTAMP and B.rn=1
where B.rn=1 and A.EMP_NO = 1234;
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;
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));
I have a chain of CTEs, and at the end I want to select from the last one twice. Oracle lets me do either of the selects but not a union of both of them (even if I do "select * from (select union select)". The only thing I could narrow down the problem to was referring to "runinfo" in two selects, but that in and of itself is not the problem as Shannon shows.
WITH lastTen AS (
SELECT id
FROM (SELECT autobuild_id, id, rank() OVER (PARTITION BY autobuild_id ORDER BY id DESC) as rank
FROM runs
WHERE status='FINISHED' AND type='FULL' AND build_failed in ('n', 'N'))
WHERE rank <= 10
),
recentAvg AS (
SELECT autobuild_id, avg(elapsed) avgtime
FROM runs
JOIN lastTen ON (runs.id = lastTen.id)
GROUP BY autobuild_id
),
runinfo AS (
SELECT autobuildid, runid, changelist, status, age
FROM (
SELECT runs.autobuild_id autobuildid, runs.id runid, changelist, runs.status status, runs.create_date, a.avgtime,
CASE WHEN status = 'RUNNING' THEN TO_NUMBER(sysdate - start_date)*86400 -- in seconds to compare to elapsed
WHEN status = 'TO BE' THEN TO_NUMBER(sysdate - create_date) -- in days
END AS age
FROM runs
LEFT JOIN recentAvg a ON (runs.autobuild_id = a.autobuild_id)
)
WHERE (status = 'RUNNING' AND age > avgtime * 1.5)
OR (status = 'TO BE' AND age > 1)
ORDER BY autobuildid, runid DESC
),
running AS (
SELECT autobuilds.id, name, runid, changelist, runinfo.status, age
FROM runinfo
JOIN autobuilds ON (runinfo.autobuildId=autobuilds.id)
WHERE runinfo.status='RUNNING'
),
tobe AS (
SELECT autobuildid, name, runid, changelist, status, age
FROM (SELECT autobuildid, name, runid, changelist, runinfo.status, age, RANK() OVER (PARTITION BY autobuildid ORDER BY runid DESC) AS rank
FROM runinfo
JOIN autobuilds ON (runinfo.autobuildid=autobuilds.id)
WHERE runinfo.status='TO BE')
WHERE rank=1
)
SELECT * FROM running
UNION ALL
SELECT * FROM tobe
What is the simpliest query that doesn't work for you?
What is the complete error message?
Oracle does allow a CTE to be referenced twice, but I am not understanding what you are attempting:
SQL> with a as (select * from dual)
2 , b as (select * from a)
3 , c as (select * from b)
4 select *
5 from b, c
6 where b.dummy = c.dummy;
D D
- -
X X