Report Job difference in HR schema - oracle

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)

Related

Group by a subquery in Oracle

Say I have a query such as the following:
SELECT
EmployeeName,
DepNo,
sum(wages)
FROM
Employees
GROUP BY
EmployeeName,
DepNo
Then I have some data in another table called Departments such as:
DepNo DepName
1 Accountants
2 HR
3 IT
How could I swap the Employee.DepNo to be the Department.DepName?
I tried adding a nested select but I ran into an issue when grouping:
SELECT
EmployeeName,
(SELECT DISTINCT DepName FROM Departments WHERE Employees.DepNo = Departments.DepNo) AS DepName,
sum(wages)
FROM
Employees
GROUP BY
EmployeeName,
DepName
Don't use a subquery, just JOIN to the other table and then return the column in the SELECT:
SELECT E.EmployeeName,
D.DepName,
SUM(Wages) AS Wages
FROM Employees E
JOIN Departments D ON E.DepNo = D.DepNo
GROUP BY E.EmployeeName,
D.DepName
Since you are asking for another answer, You may update your query to -
SELECT
EmployeeName,
(SELECT DISTINCT DepName
FROM Departments
WHERE Employees.DepNo = Departments.DepNo) AS DepName,
sum(wages)
FROM
Employees
GROUP BY
EmployeeName,
Employees.DepNo

Oracle SQL - Subquery to get the employees that receive more than the Max_Salary for their Job_Id

In HR Schema, we have a table jobs, with min and max salary for each job id. I have to find the employees that receive more than the Max_Salary for their Job_Id, using subquery.
I have tried to make this subquery, but I'm getting "no data found".
SELECT FIRST_NAME,SALARY, JOB_ID FROM HR.EMPLOYEES E WHERE SALARY >(SELECT MAX_SALARY FROM HR.JOBS J WHERE E.JOB_ID = J.JOB_ID );
Thanks in advance.
WITH SALARY_DETAIL AS (SELECT JOB_ID, MAX(MAX_SALARY) AS MAX_SALARY FROM JOBS J GROUP BY J.JOB_ID)
SELECT E.FIRST_NAME, E.SALARY, E.JOB_ID FROM HR.EMPLOYEES E , SALARY_DETAIL S
WHERE S.JOB_ID = E.JOB_ID
AND E.SALARY > S.MAX_SALAR

how to get a row number for each group in report?

i am making a report that getting employees for each group and i need to get a row number for each employee in each department group
select rownum,e.empno, e.ename, e.sal, e.comm, e.deptno, d.dname
from emp e
left join dept d on emp.deptno = dept.deptno
order by deptno;
We can try using ROW_NUMBER here:
SELECT
ROW_NUMBER() OVER (PARTITION BY emp.deptno ORDER BY emp.ename) rn,
emp.empno,
emp.ENAME,
emp.SAL,
emp.COMM,
emp.DEPTNO,
dept.dname
FROM emp
LEFT JOIN dept
ON emp.deptno = dept.deptno
ORDER BY
emp.deptno;
Note that you never provided logic for what should decide the ordering of each employee within a department. In the absence of this, I have used the employee name. If you want some other ordering, then just modify the ORDER BY clause in the call to ROW_NUMBER.

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