How to get max salary and count from employee table - sql

I have task where I need to filter in such a way that need to print the max(salary * months) and number of times the max(salary * months) occurred.
My data:
employee_id , name , months , salary
12 , Ami , 15 , 1230
14 , Bik , 1 , 678
15 , Tom , 10 , 500
16 , Bob , 12 , 3456
17 , Wil , 1 , 9000
18 , Tim , 14 , 1239
22 , Jil , 15 , 1230
Note: the table as only 4 columns -> employee_id, name, months, salary
I have written a query where I am able to get max(salary * months)
Query #1:
select
max(msal)
from
(select
employee_id,
name,
salary,
months,
(salary * months) as msal
from
employee);
Query #2:
select
e1.employee_id,
e1.months,
e1.salary,
e2.employee_id,
(e2.salary * e2.months) as earnings
from
employee e1
join
employee e2 on e1.employee_id = e2.employee_id;
Query #3:
select
emp,earnings
from
(
select
e1.employee_id as emp,
e1.months,
e1.salary,
e2.employee_id,
(e2.salary * e2.months) as earnings
from
employee e1
join employee e2 on e1.employee_id = e2.employee_id
)
order by
earnings desc;
I calculated on paper these details:
15 * 1230 -> 18450
1 * 678 -> 678
10 * 500 -> 5000
12 * 3456 -> 41472
1 * 9000 -> 9000
14 * 1239 -> 17346
15 * 1230 -> 18450
According my analysis my output should be: 41472 1
41472-> max(salary * months)
1 -> occurred twice

Here's one option:
Sample data:
SQL> with emp (employee_id, name, months, salary) as
2 (select 12, 'ami', 15, 1230 from dual union all
3 select 14, 'bik', 1 , 678 from dual union all
4 select 15, 'tom', 10, 500 from dual union all
5 select 16, 'bob', 12, 3456 from dual union all
6 select 17, 'wil', 1 , 9000 from dual union all
7 select 18, 'tim', 14, 1239 from dual union all
8 select 22, 'jil', 15, 1230 from dual
9 ),
Query begins here: rank values in descending order ...
10 temp as
11 (select salary * months max_value,
12 count(*) cnt,
13 rank() over (order by salary * months desc) rnk
14 from emp
15 group by salary * months
16 )
17 select max_value, cnt
18 from temp
19 where rnk = 1; --> ... and, finally, fetch row(s) that rank as the highest
MAX_VALUE CNT
---------- ----------
41472 1
SQL>
Although you didn't ask for it, but - such an approach is good because you can easily get - for example:
the second highest value simply by modifying the where clause to
where rnk = 2
top 3 values
where rnk <= 3
etc.

You want to look at salary * month results, count their occurence and only show the top figure. So, group by salary * month, count and take the top row:
select salary * month, count(*)
from employee
group by salary * month
order by salary * month desc
fetch first row only;

Finally I was able to write a Query
Incase any efficient way of writing Query. Please provide your solution
Query :
SELECT
msal,
COUNT(*)
FROM
(
SELECT
months * salary AS msal
FROM
employee_salary
WHERE
( months * salary ) IN (
SELECT
MAX(months * salary)
FROM
employee_salary
)
)
GROUP BY
msal;

Related

SQL select two same column on same table but with different ORDER

Trainee here.
I need to create a list last names from the same table.
say we have a table named "sample", this table only consists of:
What I want to do here is both last name column will be selected but have different order, the first column would be ascending and the second column would be descending like the photo below
Here's one option: it splits first and last name into two subqueries which use row_number analytic function. It is then used to join rows.
Lines #1 - 6 represent your sample data. Query you really need begins at line #7.
SQL> with test (last_name, first_name) as
2 (select 'L1one' , 'F1one' from dual union all
3 select 'L2two' , 'F2two' from dual union all
4 select 'L3three', 'F3hthree' from dual union all
5 select 'L4four' , 'F4four' from dual
6 ),
7 ln as
8 (select last_name,
9 row_Number() over (order by last_name) rn
10 from test
11 ),
12 fn as
13 (select first_name,
14 row_number() over (order by first_name desc) rn
15 from test
16 )
17 select l.last_name, f.first_name
18 from ln l join fn f on f.rn = l.rn
19 order by l.last_name
20 /
LAST_NA FIRST_NA
------- --------
L1one F4four
L2two F3hthree
L3three F2two
L4four F1one
SQL>
[EDIT: both last names? I thought it was a typo]
If that's so, self-join is a better option:
SQL> with test (last_name, first_name) as
2 (select 'L1one' , 'F1one' from dual union all
3 select 'L2two' , 'F2two' from dual union all
4 select 'L3three', 'F3hthree' from dual union all
5 select 'L4four' , 'F4four' from dual
6 ),
7 temp as
8 (select last_name,
9 row_number() over (order by last_name asc) rna,
10 row_number() over (order by last_name desc) rnd
11 from test
12 )
13 select a.last_name, d.last_name
14 from temp a join temp d on a.rna = d.rnd
15 order by a.last_name;
LAST_NA LAST_NA
------- -------
L1one L4four
L2two L3three
L3three L2two
L4four L1one
SQL>
Here is one way to do it. In a single subquery, assign the ordinal number (rn) based on ascending order, but also keep track of the total row count. Then follow with a join.
with
test (last_name, first_name) as (
select 'L1one' , 'F1one' from dual union all
select 'L2two' , 'F2two' from dual union all
select 'L3three', 'F3hthree' from dual union all
select 'L4four' , 'F4four' from dual
)
, prep (last_name, rn, ct) as (
select last_name, row_number() over (order by last_name), count(*) over ()
from test
)
select a.last_name as last_name_asc, b.last_name as last_name_desc
from prep a inner join prep b on a.rn + b.rn = a.ct + 1
;
LAST_NAME_ASC LAST_NAME_DESC
-------------- --------------
L1one L4four
L2two L3three
L3three L2two
L4four L1one

Create table from loop output Oracle SQL

I need to pull a random sample from a table of ~5 million observations based on 175 demographic options. The demographic table is something like this form:
1 40 4%
2 30 3%
3 30 3%
- -
174 2 .02%
175 1 .01%
Basically I need this same demographic breakdown randomly sampled from the 5M row table. For each demographic I need a sample of the same one from the larger table but with 5x the number of observations (example: for demographic 1 I want a random sample of 200).
SELECT *
FROM (
SELECT *
FROM my_table
ORDER BY
dbms_random.value
)
WHERE rownum <= 100;
I've used this syntax before to get a random sample but is there any way I can modify this as a loop and substitute variable names from existing tables? I'll try to encapsulate the logic I need in pseudocode:
for (each demographic_COLUMN in TABLE1)
select random(5*num_obs_COLUMN in TABLE1) from ID_COLUMN in TABLE2
/*somehow join the results of each step in the loop into one giant column of IDs */
You could join your tables (assuming the 1-175 demographic value exists in both, or there is an equivalent column to join on), something like:
select id
from (
select d.demographic, d.percentage, t.id,
row_number() over (partition by d.demographic order by dbms_random.value) as rn
from demographics d
join my_table t on t.demographic = d.demographic
)
where rn <= 5 * percentage
Each row in the main table is given a random pseudo-row-number within its demographic (via the analytic row_number()). The outer query then uses the relevant percentage to select how many of those randomly-ordered rows for each demographic to return.
I'm not sure I've understood how you're actually picking exactly how many of each you want, so that probably needs to be adjusted.
Demo with a smaller sample in a CTE, and matching smaller match condition:
-- CTEs for sample data
with my_table (id, demographic) as (
select level, mod(level, 175) + 1 from dual connect by level <= 175000
),
demographics (demographic, percentage, str) as (
select 1, 40, '4%' from dual
union all select 2, 30, '3%' from dual
union all select 3, 30, '3%' from dual
-- ...
union all select 174, 2, '.02%' from dual
union all select 175, 1, '.01%' from dual
)
-- actual query
select demographic, percentage, id, rn
from (
select d.demographic, d.percentage, t.id,
row_number() over (partition by d.demographic order by dbms_random.value) as rn
from demographics d
join my_table t on t.demographic = d.demographic
)
where rn <= 5 * percentage;
DEMOGRAPHIC PERCENTAGE ID RN
----------- ---------- ---------- ----------
1 40 94150 1
1 40 36925 2
1 40 154000 3
1 40 82425 4
...
1 40 154350 199
1 40 126175 200
2 30 36051 1
2 30 1051 2
2 30 100451 3
2 30 18026 149
2 30 151726 150
3 30 125302 1
3 30 152252 2
3 30 114452 3
...
3 30 104652 149
3 30 70527 150
174 2 35698 1
174 2 67548 2
174 2 114798 3
...
174 2 70698 9
174 2 30973 10
175 1 139649 1
175 1 156974 2
175 1 145774 3
175 1 97124 4
175 1 40074 5
(you only need the ID, but I'm including the other columns for context); or more succinctly:
with my_table (id, demographic) as (
select level, mod(level, 175) + 1 from dual connect by level <= 175000
),
demographics (demographic, percentage, str) as (
select 1, 40, '4%' from dual
union all select 2, 30, '3%' from dual
union all select 3, 30, '3%' from dual
-- ...
union all select 174, 2, '.02%' from dual
union all select 175, 1, '.01%' from dual
)
select demographic, percentage, count(id) as ids, min(id) as min_id, max(id) as max_id
from (
select d.demographic, d.percentage, t.id,
row_number() over (partition by d.demographic order by dbms_random.value) as rn
from demographics d
join my_table t on t.demographic = d.demographic
)
where rn <= 5 * percentage
group by demographic, percentage
order by demographic;
DEMOGRAPHIC PERCENTAGE IDS MIN_ID MAX_ID
----------- ---------- ---------- ---------- ----------
1 40 200 175 174825
2 30 150 1 174126
3 30 150 2452 174477
174 2 10 23448 146648
175 1 5 19074 118649
db<>fiddle

How to Get Set of Second Highest Values?

Suppose I have the following table:
employee_id salary
34 100
22 49
19 49
29 30
17 22
And I want to return the set of employees with the second highest salaries (when there are ties), as follows:
employee_id salary
22 49
19 49
How would I do that?
Use DENSE_RANK:
SELECT employee_id, salary
FROM
(
SELECT employee_id, salary, DENSE_RANK() OVER (ORDER BY salary DESC) dr
FROM yourTable
) t
WHERE dr = 2;
You can use nested query.
Steps taken :
Get all salary values ( sort it and obtain 2nd highest value ) :
SELECT salary FROM employee GROUP BY 1 ORDER BY 1 DESC limit 1 OFFSET 1;
OR can be written as :
SELECT salary FROM employee GROUP BY employee_id ORDER BY employee_id DESC limit 1 OFFSET 1;
Now use the query within employee table
SELECT * FROM employee where salary=(SELECT salary FROM employee GROUP BY 1 ORDER BY 1 DESC limit 1 OFFSET 1);
this can be done also in using query below,
scenario 1: Output two records
WITH employee
AS (
SELECT 34 emp_id, 100 rate FROM DUAL
UNION
SELECT 22 emp_id, 49 rate FROM DUAL
UNION
SELECT 19 emp_id, 49 rate FROM DUAL
UNION
SELECT 29 emp_id, 30 rate FROM DUAL
UNION
SELECT 17 emp_id, 22 rate FROM DUAL),
emp_rate_cnt AS
(SELECT rownum rown, rate, same_rate_count
FROM (SELECT rate, count(1) same_rate_count
FROM employee
GROUP BY rate
ORDER BY rate DESC))
SELECT *
FROM employee a
WHERE exists (SELECT 1
FROM emp_rate_cnt b
WHERE b.rate = a.rate
AND b.rown = 2
AND b.same_rate_count > 1);
scenario 2: Output no records
WITH employee
AS (
SELECT 34 emp_id, 100 rate FROM DUAL
UNION
SELECT 22 emp_id, 49 rate FROM DUAL
UNION
SELECT 19 emp_id, 50 rate FROM DUAL
UNION
SELECT 29 emp_id, 30 rate FROM DUAL
UNION
SELECT 17 emp_id, 22 rate FROM DUAL),
emp_rate_cnt AS
(SELECT rownum rown, rate, same_rate_count
FROM (SELECT rate, count(1) same_rate_count
FROM employee
GROUP BY rate
ORDER BY rate DESC))
SELECT *
FROM employee a
WHERE exists (SELECT 1
FROM emp_rate_cnt b
WHERE b.rate = a.rate
AND b.rown = 2
AND b.same_rate_count > 1);
I hope this is the easiest of all. Use rownum as it is Oracle.
SELECT t.employee_id, t.salary
FROM
(
SELECT distinct employee_id, salary, rownum as row from
FROM yourTable order by salary desc
) t
WHERE t.row = 2;

Find the employee with max salary in his department

Table structure is as below
id dept salary
1 30 2000
2 20 5500
3 30 6700
4 30 8900
5 30 9900
6 10 1120
7 20 8900
8 10 2400
9 30 2600
10 10 2999
I need the output to have two columns:
Id and Salary
Id should be unique and salary should have the max salary
Although data structure is a bit strange, the query below will give the desired result:
create table #tmp(
id numeric(10,0),
dept int,
salary numeric(10,0))
insert #tmp select 1, 30, 2000
insert #tmp select 2, 20, 5500
insert #tmp select 3, 30, 6700
insert #tmp select 4, 30, 8900
insert #tmp select 5, 30, 9900
insert #tmp select 6, 10, 1120
insert #tmp select 7, 20, 8900
insert #tmp select 8, 10, 2400
insert #tmp select 9, 30, 2600
insert #tmp select 10, 10, 2999
select * from #tmp order by dept
id dept salary
------------ ----------- ------------
6 10 1120
8 10 2400
10 10 2999
2 20 5500
7 20 8900
1 30 2000
9 30 2600
3 30 6700
4 30 8900
5 30 9900
select id, salary
from #tmp t1, (select dept=dept, salaryMax=max(salary) from #tmp group by dept) t2
where t1.dept = t2.dept
and t1.salary = t2.salaryMax
id salary
------------ ------------
7 8900
10 2999
5 9900
SELECT ID,Salary FROM TABLE_NAME WHERE SALARY =(SELECT MAX(SALARY) FROM TABLE_NAME)
SELECT a.Id, a.Salary
FROM table a
WHERE a.Salary = (SELECT MAX(Salary) FROM table
WHERE Id = a.Id)
select * from (select * from dep order by salary desc, dep, id) t
group by t.dep
Use NOT EXISTS to return an epmloyee if no other within same department has a higher salary.
select * from table t1
where not exists (select 1 from table t2
where t2.dept = t1.dept
and t2.salary > t1.salary)
Core SQL-99, will work with both Oracle and Sybase!
Alternative solution:
select * from table
where (dept, salary) = (select dept, max(salary)
from table
group by dept)
Using the following feature outside Core SQL-2003: F641, "Row and table constructors". (Don't know what Sybase and Oracle supports here...)
Using single scan:
WITH SALARIES AS (
SELECT 1 ID, 30 DEPT, 2000 SALARY FROM DUAL UNION ALL
SELECT 2 ID, 20 DEPT, 5500 SALARY FROM DUAL UNION ALL
SELECT 3 ID, 30 DEPT, 6700 SALARY FROM DUAL UNION ALL
SELECT 4 ID, 30 DEPT, 8900 SALARY FROM DUAL UNION ALL
SELECT 5 ID, 30 DEPT, 9900 SALARY FROM DUAL UNION ALL
SELECT 6 ID, 10 DEPT, 1120 SALARY FROM DUAL UNION ALL
SELECT 7 ID, 20 DEPT, 8900 SALARY FROM DUAL UNION ALL
SELECT 8 ID, 10 DEPT, 2400 SALARY FROM DUAL UNION ALL
SELECT 9 ID, 30 DEPT, 2600 SALARY FROM DUAL UNION ALL
SELECT 10 ID, 10 DEPT, 2999 SALARY FROM DUAL
)
SELECT
MAX(ID) KEEP (DENSE_RANK LAST ORDER BY SALARY) ID,
MAX(SALARY) SALARY
FROM
SALARIES
GROUP BY
DEPT;
All the below three queries worked :
select t.dept, t.salary
from
(select dept,max(salary) as m_sal from tempdb..test
group by dept)x, tempdb..test t
where x.dept = t.dept
and x.m_sal = t.salary
select dept, id, salary as m_sal
from tempdb..test t1
where salary = (select max(salary) from tempdb..test t2 where t2.dept = t1.dept)
select t1.dept,t1.salary from tempdb..test t1
inner join
(select dept,max(salary) as m_sal from tempdb..test
group by dept)x on
t1.salary=x.m_sal and
t1.dept=x.dept

Oracle SQL to generate interleaved SQL results

Hi i am looking for a way to write a SQL statement which will come out with the following results :-
Lets say we have Dept & Emp ID i would like to generate like records from Dept 3 for the first two rows then followed by Dept 2 with one row then continue Dept 3 and so on :
DEPT EMPID
----- ------
3 1
3 2
2 3
3 7
3 8
2 9
Thank You.
You could use something like this
SELECT
DEPT,
EMPID
FROM (
SELECT
*,
ceil((row_number() OVER (PARTITION BY dept ORDER BY EMPID ))/ 2::numeric(5,2)) AS multiple_row_dept,
row_number() OVER (PARTITION BY dept ORDER BY EMPID ) AS single_row_dept
FROM
test_data2
) sub_query
ORDER BY
CASE
WHEN DEPT = 2 THEN single_row_dept
ELSE multiple_row_dept
END,
DEPT DESC,
EMPID
single_row_dept specifics which dept should appear only once, in this case its DEPT 2 followed by multiple other departments
First order a table by empid in a subquery,
then calculate a remainder of rowids divided by 3,
then depending on a result of calculation, return 2 or 3, using case expression,
like this
SELECT
CASE REMAINDER( rownum, 3 )
WHEN 0 THEN 2
ELSE 3
END As DeptId,
empid
FROM (
SELECT empid
FROM table1
ORDER BY empid
)
demo: http://sqlfiddle.com/#!4/bd1bb/3
The following code has some limitations:
1) There are only 2 DepId's in the source table
2) Ratio between records having different DepId is exactly 2:1
3) Ordering in one place of the query should be changed depending whether DeptId having more records is naturally greater then the other or not
with t as (
select 3 dept_id, 1 emp_id from dual
union all
select 3, 2 from dual
union all
select 3, 7 from dual
union all
select 3, 8 from dual
union all
select 2, 3 from dual
union all
select 2, 9 from dual)
select dept_id, emp_id
from (select dept_id,
emp_id,
dense_rank() over(partition by dept_id order by in_num + mod(in_num, out_num)) ord
from (select dept_id,
emp_id,
row_number() over(partition by dept_id order by emp_id) in_num,
/*in the following ORDER BY change to DESC or ASC depending on which dept_id has more records*/
dense_rank() over(order by dept_id) out_num
from t))
order by ord, dept_id desc;
DEPT_ID EMP_ID
---------- ----------
3 1
3 2
2 3
3 8
3 7
2 9