List the branch that monthly pays the most in salaries - sql

I have this table, the expected output should be B003 since it's pays 54,000
STAFF
SALARY
BRAN
SL21
30000
B005
SG37
12000
B003
SG14
18000
B003
SA9
9000
B007
SG5
24000
B003
SL41
9000
B005
So far I only have this subquery, which isn't working how I expected.
SELECT BRANCHNO
FROM STAFF
WHERE (SALARY) IN (SELECT MAX(SUM(SALARY))
FROM STAFF
GROUP BY BRANCHNO);
This works but I want a subquery that returns the branchno
SELECT MAX(SUM(SALARY))
FROM STAFF
GROUP BY BRANCHNO;

select BRANCHNO max(sum_sal)
from (SELECT BRANCHNO, SUM(SALARY) sum_sal
FROM STAFF
GROUP BY BRANCHNO) q1
group by BRANCHNO ;
The column used to group the rows can be displayed. So, add BRANCHNO to your select clause.

One option is to use rank analytic function which ranks branches by sum of their salaries in descending order; you'd then return the one(s) that rank as the highest (rnk = 1).
Sample data:
SQL> with staff (staff, salary, bran) as
2 (select 'SL21', 30000, 'B005' from dual union all
3 select 'SG37', 12000, 'B003' from dual union all
4 select 'SG14', 18000, 'B003' from dual union all
5 select 'SA9' , 9000, 'B007' from dual union all
6 select 'SG5' , 24000, 'B003' from dual union all
7 select 'SL41', 9000, 'B005' from dual
8 )
Query:
9 select bran
10 from (select bran, rank() over (order by sum(salary) desc) rnk
11 from staff
12 group by bran
13 )
14 where rnk = 1;
BRAN
----
B003
SQL>

Related

Generating random student data

I'm trying to create a process that populates a student table. I want to be able to create a different combination of a student's first/last name and dob every time I run the query.
The code below appears to work fine as it generates 5 names. My first question is can this be modified to generate N NUMBER of rows sat for example 20. I tried using CONNECT by level <=20 but that gives me a syntax error.
Secondly, know the random_date function works
Select random_date(DATE '2001-01-01', DATE '2001-12-31') from dual
17-NOV-2001 08:31:16
But I can't seem to incorporate into my SQL.
Any help would be greatly appreciated. Thanks in advance for your expertise and time
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'DD-MON-YYYY HH24:MI:SS.FF';
ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-YYYY HH24:MI:SS';
CREATE OR REPLACE FUNCTION random_date(
p_from IN DATE,
p_to IN DATE
) RETURN DATE
IS
BEGIN
RETURN p_from + DBMS_RANDOM.VALUE() * (p_to - p_from + 1 );
END random_date;
/
CREATE TABLE students (
student_id number(*,0),
first_name VARCHAR(25) NOT NULL,
last_name VARCHAR(25) NOT NULL,
dob DATE,
constraint teacher_pk primary key (student_id));
WITH raw_names (first_name, last_name) AS
(
SELECT 'Faith', 'Andrews' FROM dual UNION ALL
SELECT 'Tom', 'Thornton' FROM dual UNION ALL
SELECT 'Anna', 'Smith' FROM dual UNION ALL
SELECT 'Lisa', 'Jones' FROM dual UNION ALL
SELECT 'Andy', 'Beirs' FROM dual
)
, numbered_names AS
(
SELECT first_name, last_name
, ROW_NUMBER () OVER (ORDER BY dbms_random.value (0, 1)) AS first_num
, ROW_NUMBER () OVER (ORDER BY dbms_random.value (0, 2)) AS last_num
FROM raw_names
)
SELECT fn.first_num AS student_id
, fn.first_name
, ln.last_name
FROM numbered_names fn
JOIN numbered_names ln ON ln.last_num = fn.first_num
ORDER BY student_id
;
I can't debug your code as you didn't post it (the one that raised a syntax error and doesn't accept the function).
Anyway, here's what you might do:
line #14 - function call
lines #15 - 19 show how to create desired number of rows (must be multiple of number of rows in raw_names)
SQL> WITH raw_names (first_name, last_name) AS
2 (
3 SELECT 'Faith', 'Andrews' FROM dual UNION ALL
4 SELECT 'Tom', 'Thornton' FROM dual UNION ALL
5 SELECT 'Anna', 'Smith' FROM dual UNION ALL
6 SELECT 'Lisa', 'Jones' FROM dual UNION ALL
7 SELECT 'Andy', 'Beirs' FROM dual
8 )
9 , numbered_names AS
10 (
11 SELECT first_name, last_name
12 , ROW_NUMBER () OVER (ORDER BY dbms_random.value (0, 1)) AS first_num
13 , ROW_NUMBER () OVER (ORDER BY dbms_random.value (0, 2)) AS last_num
14 , random_date (date '2001-01-01', date '2001-12-31') datum
15 FROM raw_names cross join
16 table(cast(multiset(select level from dual
17 connect by level <= (select &n / count(*) from raw_names))
18 as sys.odcinumberlist))
19 )
20 SELECT fn.first_num AS student_id
21 , fn.first_name
22 , ln.last_name
23 , ln.datum
24 FROM numbered_names fn
25 JOIN numbered_names ln ON ln.last_num = fn.first_num
26 ORDER BY student_id
27 ;
Enter value for n: 20
Result:
STUDENT_ID FIRST LAST_NAM DATUM
---------- ----- -------- --------------------
1 Tom Andrews 12-NOV-2001 14:42:05
2 Faith Jones 06-MAR-2001 05:14:07
3 Tom Thornton 04-SEP-2001 16:28:25
4 Faith Beirs 29-MAR-2001 06:11:35
5 Andy Thornton 18-MAY-2001 17:32:07
6 Andy Jones 19-JAN-2001 19:39:15
7 Anna Jones 17-JAN-2001 02:51:39
8 Andy Andrews 31-DEC-2001 15:36:44
9 Faith Beirs 22-JUN-2001 05:34:22
10 Lisa Thornton 29-JUL-2001 07:00:15
11 Lisa Smith 31-JAN-2001 04:17:04
12 Anna Andrews 07-FEB-2001 09:02:21
13 Lisa Thornton 31-DEC-2001 20:18:06
14 Lisa Smith 24-SEP-2001 04:10:21
15 Tom Andrews 30-JUN-2001 12:01:04
16 Faith Jones 16-AUG-2001 19:56:54
17 Anna Beirs 23-NOV-2001 11:01:03
18 Anna Beirs 23-NOV-2001 08:33:39
19 Andy Smith 24-SEP-2001 21:27:00
20 Tom Smith 24-SEP-2001 22:07:39
20 rows selected.
SQL>

Need sql query to get expected output for the below,

Tried the below query, but it works only for the first and second records.
Select
name,
dept,
sal,
(
coalesce(sal, 0) + coalesce(saltodrop)
) as running total (
SELECT
name,
dept,
sal,
LAG(Sal, 1, 0) OVER(
PARTITION BY [dept]
ORDER BY
[name],
[dept] ASC
) AS [saltodrop]
FROM
dataset
) as data_set_extract
Input
Name dept sal
John sales 10000
Tom sales 8000
Tim sales 5000
George finance 6000
Dane finance 4000
Mike hr 5000
Meme hr 6000
Ark it 5000
Output
Name dept sal
John sales 1000
Tom sales 18000
Tim sales 23000
George finance 29000
Dane finance 33000
Mike hr 38000
Meme hr 44000
Ark it 49000
Using the Oracle database, I need to add a consecutive row of the
first two records, later the sum of the first record and second record
with that of the third record and so on.
Assuming you have already ordered the results then use the SUM analytic function and order by ROWNUM to keep the current ordering:
SELECT t.*,
SUM(sal) OVER (ORDER BY ROWNUM) AS cumulative_sal
FROM table_name t;
Which, for the sample data:
CREATE TABLE table_name (Name, dept, sal) AS
SELECT 'John', 'sales', 10000 FROM DUAL UNION ALL
SELECT 'Tom', 'sales', 8000 FROM DUAL UNION ALL
SELECT 'Tim', 'sales', 5000 FROM DUAL UNION ALL
SELECT 'George', 'finance', 6000 FROM DUAL UNION ALL
SELECT 'Dane', 'finance', 4000 FROM DUAL UNION ALL
SELECT 'Mike', 'hr', 5000 FROM DUAL UNION ALL
SELECT 'Meme', 'hr', 6000 FROM DUAL UNION ALL
SELECT 'Ark', 'it', 5000 FROM DUAL;
Outputs:
NAME
DEPT
SAL
CUMULATIVE_SAL
John
sales
10000
10000
Tom
sales
8000
18000
Tim
sales
5000
23000
George
finance
6000
29000
Dane
finance
4000
33000
Mike
hr
5000
38000
Meme
hr
6000
44000
Ark
it
5000
49000
db<>fiddle here

Query required for given case

I have two tables one with name, Employee_Info which contains Employee_no, Name and Department and other table with name Attendance which contains Employee_no and Attendance_Date. Attendance table contains data of those employees who have marked their attendance.
Now I want a query which can help me select those employees who are absent on a specific date in the format as below:
Employee_no Department Absent_on
10160100000 XYZ 06/25/2021
Kindly help!!
Here's one option. Read comments within code (sample data in lines #1 - 14; query you might want to use begins at line #15).
SQL> with
2 -- sample data
3 employee_info (employee_no, name, department) as
4 (select 1, 'Little', 10 from dual union all
5 select 2, 'Foot' , 20 from dual
6 ),
7 attendance (employee_no, attendance_date) as
8 (select 1, date '2021-09-01' from dual union all
9 select 1, date '2021-09-02' from dual union all
10 select 1, date '2021-09-03' from dual union all
11 -- employee 2 was absent on 2021-09-02
12 select 2, date '2021-09-01' from dual union all
13 select 2, date '2021-09-03' from dual
14 ),
15 missing_attendance_info as
16 -- list of employees who were absent
17 (select e.employee_no, a.attendance_date
18 from attendance a cross join employee_info e
19 minus
20 select b.employee_no, b.attendance_date
21 from attendance b
22 )
23 -- finally, join employee_info with employees who were absent
24 select m.employee_no, e.name, e.department, m.attendance_date
25 from missing_attendance_info m join employee_info e
26 on e.employee_no = m.employee_no;
EMPLOYEE_NO NAME DEPARTMENT ATTENDANCE
----------- ------ ---------- ----------
2 Foot 20 2021-09-02
SQL>
Well you can do it like this,
SELECT EMP.EMPLOYEE_NO AS EMPLOYEE_NO, EMP.DEPARTMENT AS DEPARTMENT, :IN_DATE AS ABSENT_ON
FROM EMPLOYEE_INFO EMP
WHERE
EMP.EMPLOYEE_NO NOT IN (SELECT EMP.EMPLOYEE_NO
FROM ATTENDANCE ATT
LEFT JOIN EMPLOYEE_INFO EMP
ON ATT.EMPLOYEE_NO = EMP.EMPLOYEE_NO
WHERE
TO_CHAR(ATT.ATTENDANCE_DATE, 'MM/DD/YYYY') = :IN_DATE);

Need help understanding range between in SQL window functions

I am trying understand how range clause is working in below case (oracle database)
SELECT
EMPID,NAME,
HIRE_DATE_1,
SALARY,
count(1) over(order by HIRE_DATE_1 range between 1 preceding and 1 preceding) as PREV_MIN_SA
FROM (
SELECT
EMPID,
NAME,
(EXTRACT(year from HIRE_DATE)*10000)+(EXTRACT(MONTH FROM HIRE_DATE) * 100) + (extract(DAY from HIRE_DATE)) as HIRE_DATE_1,SALARY
FROM EMPLOYEE A order by HIRE_DATE,SALARY
) A
ORDER BY HIRE_DATE_1
Result Set :
EMPID NAME HIRE_DATE_1 SALARY PREV_MIN_SA
100 Ravi 20180101 5000 0
101 Kumar 20180101 7000 0
102 Satish 20180101 13000 0
103 Naresh 20180102 7500 3
105 Lalith 20180104 17300 0
104 Suresh 20180104 40000 0
106 Latha 20180201 16000 0
The inner query is just converting date into numeric YYYYMMDD format.
My intention is to get the count of people who joined immediately prior to the date of the employee in each record. I can take the count of rows with same HIRE_DATE and use LAG function but somehow not understand how the sql is returning this result set.
Also, once I am done with the counts I would like to get the MIN(SALARY) of the employees who joined immediately prior to the employee in current row and find the difference in salaries so wondering if somehow I can define the window to only have all records with immediately prior HIRE_DATE.
Thanks
This should get the preceding hire date...
SELECT
EMPID,NAME, HIRE_DATE, SALARY,
MAX(HIRE_DATE) OVER (ORDER BY HIRE_DATE
RANGE BETWEEN UNBOUNDED PRECEDING
AND INTERVAL '1 DAY' PRECEDING
)
AS PREV_HIRE_DATE
FROM
EMPLOYEE
Then I think you need to join back on to the employee table to find the number of employees and their min salary?
SELECT
*
FROM
(
SELECT
EMPID,NAME, HIRE_DATE, SALARY,
MAX(HIRE_DATE) OVER (ORDER BY HIRE_DATE
RANGE BETWEEN UNBOUNDED PRECEDING
AND INTERVAL '1 DAY' PRECEDING
)
AS PREV_HIRE_DATE
FROM
EMPLOYEE
)
EMPS
LEFT JOIN
(
SELECT
HIRE_DATE,
COUNT(*) AS COUNT_EMPS,
MIN(SALARY) AS MIN_SALARY
FROM
EMPLOYEE
GROUP BY
HIRE_DATE
)
PREV_EMPS
ON PREV_EMPS.HIRE_DATE = EMPS.PREV_HIRE_DATE
EDIT:
Maybe try something like this? (I have to run, good luck!)
WITH
ranked AS
(
SELECT
*,
DENSE_RANK() OVER (ORDER BY HIRE_DATE) AS HIRE_SEQ_ID
FROM
EMPLOYEE
)
SELECT
*,
MIN(SALARY) OVER (ORDER BY HIRE_SEQ_ID
RANGE BETWEEN 1 PRECEDING AND 1 PRECEDING
)
AS PREV_MIN_SALARY,
COUNT(*) OVER (ORDER BY HIRE_SEQ_ID
RANGE BETWEEN 1 PRECEDING AND 1 PRECEDING
)
AS COUNT_PREV_EMPS
FROM
ranked
This does what you're after, I believe (I've left it as an exercise for you to find the difference between the max and min salaries!):
WITH employee AS (SELECT 100 empid, 'Ravi' NAME, to_date('01/01/2018', 'dd/mm/yyyy') hire_date, 5000 salary FROM dual UNION ALL
SELECT 101 empid, 'Kumar' NAME, to_date('01/01/2018', 'dd/mm/yyyy') hire_date, 7000 salary FROM dual UNION ALL
SELECT 102 empid, 'Satish' NAME, to_date('01/01/2018', 'dd/mm/yyyy') hire_date, 13000 salary FROM dual UNION ALL
SELECT 103 empid, 'Naresh' NAME, to_date('02/01/2018', 'dd/mm/yyyy') hire_date, 7500 salary FROM dual UNION ALL
SELECT 104 empid, 'Lalith' NAME, to_date('04/01/2018', 'dd/mm/yyyy') hire_date, 17300 salary FROM dual UNION ALL
SELECT 105 empid, 'Suresh' NAME, to_date('04/01/2018', 'dd/mm/yyyy') hire_date, 40000 salary FROM dual UNION ALL
SELECT 106 empid, 'Latha' NAME, to_date('01/02/2018', 'dd/mm/yyyy') hire_date, 16000 salary FROM dual)
SELECT empid,
NAME,
hire_date,
salary,
COUNT(*) OVER (ORDER BY hire_date RANGE BETWEEN 1 PRECEDING AND 1 PRECEDING) prev_day_hire_count,
MIN(salary) OVER (ORDER BY hire_date RANGE BETWEEN 1 PRECEDING AND 1 PRECEDING) prev_day_min_sal,
MAX(salary) OVER (ORDER BY hire_date RANGE BETWEEN 1 PRECEDING AND 1 PRECEDING) prev_day_max_sal
FROM employee;
EMPID NAME HIRE_DATE SALARY PREV_DAY_HIRE_COUNT PREV_DAY_MIN_SAL PREV_DAY_MAX_SAL
---------- ------ ----------- ---------- ------------------- ---------------- ----------------
100 Ravi 01/01/2018 5000 0
101 Kumar 01/01/2018 7000 0
102 Satish 01/01/2018 13000 0
103 Naresh 02/01/2018 7500 3 5000 13000
104 Lalith 04/01/2018 17300 0
105 Suresh 04/01/2018 40000 0
106 Latha 01/02/2018 16000 0

how to retrieve highest and lowest salary from following table?

I have employee table
EMP_ID | F_NAME | L_NAME | SALARY | JOINING_DATE | DEPARTMENT
-----------------------------------------------------------------------------------
101 | John | Abraham | 100000 | 01-JAN-14 09.15.00.000000 AM | Banking
102 | Michel | Clarke | 800000 | | Insaurance
102 | Roy | Thomas | 70000 | 01-FEB-13 12.30.00.000000 PM | Banking
103 | Tom | Jose | 600000 | 03-FEB-14 01.30.00.000000 AM | Insaurance
105 | Jerry | Pinto | 650000 | 01-FEB-13 12.00.00.000000 PM | Services
106 | Philip | Mathew | 750000 | 01-JAN-13 02.00.00.000000 AM | Services
107 | TestName1 | 123 | 650000 | 01-JAN-13 12.05.00.000000 PM | Services
108 | TestName2 | Lname% | 600000 | 01-JAN-13 12.00.00.000000 PM | Insaurance
i want to find highest and lowest salary from above table in oracle sql.
if i do
select max(salary) from (select * from (select salary from employee) where rownum <2);
it returns MAX(SALARY) = 100000 where it should return 800000
If I do
select max(salary)
from (select * from (select salary from employee)
where rownum <3);
it returns MAX(SALARY) = 800000
If I do
select min(salary)
from (select * from(select salary from employee)
where rownum < 2);
it will return MIN(SALARY) = 100000 where it should return 70000.
What is wrong in this query?
what should be the correct query?
You don't need all these subqueries:
SELECT MAX(salary), MIN(salary)
FROM employee
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE employee ( EMP_ID, F_NAME, L_NAME, SALARY, JOINING_DATE, DEPARTMENT ) AS
SELECT 101, 'John', 'Abraham', 100000, TIMESTAMP '2014-01-01 09:15:00', 'Banking' FROM DUAL
UNION ALL SELECT 102, 'Michel', 'Clarke', 800000, NULL, 'Insurance' FROM DUAL
UNION ALL SELECT 102, 'Roy', 'Thomas', 70000, TIMESTAMP '2013-02-01 12:30:00', 'Banking' FROM DUAL
UNION ALL SELECT 103, 'Tom', 'Jose', 600000, TIMESTAMP '2014-02-03 01:30:00', 'Insurance' FROM DUAL
UNION ALL SELECT 105, 'Jerry', 'Pinto', 650000, TIMESTAMP '2013-02-01 12:00:00', 'Services' FROM DUAL
UNION ALL SELECT 106, 'Philip', 'Mathew', 750000, TIMESTAMP '2013-01-01 02:00:00', 'Services' FROM DUAL
UNION ALL SELECT 107, 'TestName1', '123', 650000, TIMESTAMP '2013-01-01 12:05:00', 'Services' FROM DUAL
UNION ALL SELECT 108, 'TestName2', 'Lname%', 600000, TIMESTAMP '2013-01-01 12:00:00', 'Insurance' FROM DUAL;
Query 1 - To find the highest-n salaries:
SELECT *
FROM (
SELECT salary
FROM employee
ORDER BY salary DESC
)
WHERE rownum <= 3 -- replace with the number of salaries you want to retrieve.
Results:
| SALARY |
|--------|
| 800000 |
| 750000 |
| 650000 |
Query 2 - To find the lowest-n salaries:
SELECT *
FROM (
SELECT salary
FROM employee
ORDER BY salary ASC
)
WHERE rownum <= 3 -- replace with the number of salaries you want to retrieve.
Results:
| SALARY |
|--------|
| 70000 |
| 100000 |
| 600000 |
For each row returned by a query, the ROWNUM pseudocolumn returns a number indicating the order in which Oracle selects the row from a table or set of joined rows. The first row selected has a ROWNUM of 1, the second has 2, and so on.
So in your case :
select max(salary) from (select * from (select salary from employee) where rownum <2);
This query will return
101 John Abraham 100000 01-JAN-14 09.15.00.000000 AM Banking
only this row as output... and hence the max value will be 100000 only.
select max(salary) from (select * from (select salary from employee) where rownum <3);
This query will tak first 2 rows from your table, i.e.,
101 John Abraham 100000 01-JAN-14 09.15.00.000000 AM Banking
102 Michel Clarke 800000 Insaurance
and hence the max salary will be 800000.
Similarly,
select min(salary)from (select * from(select salary from employee)where rownum<2);
will only select 1st row
select min(salary)from (select * from(select salary from employee)where rownum<2);
so min salary will be 100000.
P.S. : You could simply write your queries like this :
select max(salary) from employee where rownum<[n];
where n will be ROWNUM to which you want to limit the number of rows returned by your query
Try it:
select *
from (
select T.*, rownum RRN
from (
select salary
from employee
order by salary desc) T)
where RRN < 3
so you want the 2nd highest and 2nd lowest salary? Check this out
select max(salary), min(salary) from employee
where salary < (select max(salary) from employee)
and salary > (select min(salary) from employee)
;
1) For lowest salary.
select * from (
select empno,job,ename,sal
from emp order by sal)
where rownum=1;
2) For Highest salary.
select * from (
select empno,job,ename,sal
from emp order by sal desc)
where rownum=1;
i don't know why you make complicated queries you can simply write this and get the same result:
select salary
from employees
where rownum <=3
order by salary desc;
you can solve your problem with following queries:
Highest salary:
Select * from Employee(Select salary from Employee ORDER BY salary DISC) where rownum=1;
Lowest salary:
Select * from Employee(Select salary from Employee ORDER BY salary) where rownum=1;
Second highest salary:
Select MAX(Salary) from Employee
Where Salary < (Select MAX(Salary) from employee);
Second Lowest salary :
Select MIN(Salary) from Employee
Where Salary > (Select MIN(Salary) from employee);