oracle database query for getting monthly salary and total salary - sql

I have a table where monthly salary of employees are stored.
create table myemp
(
empno number ,
month number,
year number,
salary number
);
Now i need a query to get results like below
empno|month|Year|salary
0001 2 2016 10000
0001 3 2016 11000
0001 4 2016 12000
0001 -- ---- (10000+11000+12000)
0002 2 2016 15000
0002 3 2016 16000
0002 4 2016 15000
0002 -- ----(15000+16000+15000)

We can set total and subtotal using Rollup function of oracle like given below
select empno,month,year,sum(salary) from myemp
GROUP BY year,ROLLUP (empno,month)
here empno and month are in rollup function that gives total and subtotal of
empno and month group.
i hope this will help.

Here you go:
SELECT *
FROM (
(
SELECT empno, month, year, salary
FROM myemp
)
UNION ALL
(
SELECT empno, NULL AS month, NULL AS year, sum(salary)
FROM myemp
GROUP BY empno
)
) AS foo
ORDER BY empno, year IS NULL, year, month

would look like this
select lastname , dept_no, salary,
sum(salary) over (partition by dept_no order by lastname) dept_total
from myemp
order by salary, lastname;

Related

Analytic functions and means of window clause

I'm using Oracle and SQL Developer. I have downloaded HR schema and need to do some queries with it. Now I'm working with table Employees. As an user I need to see employees with the highest gap between their salary and the average salary of all later hired colleagues in corresponding department. It seems quite interesting and really complicated. I have read some documentation and tried, for example LEAD(), that provides access to more than one row of a table at the same time:
SELECT
employee_id,
first_name
|| ' '
|| last_name,
department_id,
salary,
hire_date,
LEAD(hire_date)
OVER(PARTITION BY department_id
ORDER BY
hire_date DESC
) AS Prev_hiredate
FROM
employees
ORDER BY
department_id,
hire_date;
That shows for every person in department hiredate of later hired person. Also I have tried to use window clause to understand its concepts:
SELECT
employee_id,
first_name
|| ' '
|| last_name,
department_id,
hire_date,
salary,
AVG(salary)
OVER(PARTITION BY department_id
ORDER BY
hire_date ROWS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING
) AS avg_sal
FROM
employees
ORDER BY
department_id,
hire_date;
The result of this query will be:
However, it is not exactly what I need. I need to reduce the result just by adding column with gap (salary-avr_sal), where the gap will be highest and receive one employee per department. How should the result look like: for example, we have 60 department. We have 5 employees there ordering by hire_date. First has salary 4800, second – 9000, third – 4800, fourth – 4200, fifth – 6000. If we do calculations: 4800 - ((9000+4800+4200+6000)/4)=-1200, 9000-((4800+4200+6000)/3)=4000, 4800 -((4200+6000)/2)=-300, 4200 - 6000=-1800 and the last person in department will have the highest gap: 6000 - 0 = 6000. Let's take a look on 20 department. We have two people there: first has salary 13000, second – 6000. Calculations: 13000 - 6000 = 7000, 6000 - 0 = 6000. The highest gap will be for first person. So for department 20 the result should be person with salary 13000, for department 60 the result should be person with salary 6000 and so on.
How should look my query to get the appropriate result (what I need is marked bold up, also I want to see column with highest gap, can be different solutions with analytic functions, but should be necessarily included window clause)?
You can get the average salary of employees that were hired prior to the current employee by just adapting the rows clause of your avg:
AVG(salary) OVER(
PARTITION BY department_id
ORDER BY hire_date
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
) AS avg_salary
The 1 PRECEDING clause tells the database not to include the current row in the window.
If you are looking for the employees with the greatest gap to that average, we can just order by the resultset:
SELECT e.*,
AVG(salary) OVER(
PARTITION BY department_id
ORDER BY hire_date
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
) AS avg_salary
FROM employees e
ORDER BY ABS(salary - avg_salary) DESC;
Finally, if you want the top "outlier salary" per department, then we need at least one more level. The shortest way to express this probably is to use ROW_NUMBER() to rank employees in each department by their salary gap to the average, and then to fetch all top rows per group using WITH TIES:
SELECT *
FROM (
SELECT e.*,
AVG(salary) OVER(
PARTITION BY department_id
ORDER BY hire_date
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
) AS avg_salary
FROM employees e
) e
ORDER BY ROW_NUMBER() OVER(
PARTITION BY department_id
ORDER BY ABS(salary - avg_salary) DESC
)
FETCH FIRST ROW WITH TIES
Maybe this is what you are looking for.
Sample data:
WITH
emp (ID, EMP_NAME, HIRE_DATE, SALARY, DEPT) AS
(
Select 601, 'HILLER', To_Date('23-JAN-82', 'dd-MON-yy'), 4800, 60 From Dual Union All
Select 602, 'MILLER', To_Date('23-FEB-82', 'dd-MON-yy'), 9000, 60 From Dual Union All
Select 603, 'SMITH', To_Date('23-MAR-82', 'dd-MON-yy'), 4800, 60 From Dual Union All
Select 604, 'FORD', To_Date('23-APR-82', 'dd-MON-yy'), 4200, 60 From Dual Union All
Select 605, 'KING', To_Date('23-MAY-82', 'dd-MON-yy'), 6000, 60 From Dual Union All
Select 201, 'SCOT', To_Date('23-MAR-82', 'dd-MON-yy'), 13000, 20 From Dual Union All
Select 202, 'JONES', To_Date('23-AUG-82', 'dd-MON-yy'), 6000, 20 From Dual
),
Create CTE named grid with several analytic functions and windowing clauses. They are not all needed but the resulting dataset below shows the logic with all components included.
grid AS
(
Select
g.*, Max(GAP) OVER(PARTITION BY DEPT) "DEPT_MAX_GAP"
From
(
Select
ROWNUM "RN",
Sum(1) OVER(Partition By DEPT Order By DEPT, HIRE_DATE, ID ROWS BETWEEN Unbounded Preceding And Current Row) "RN_DEPT",
ID, EMP_NAME, HIRE_DATE, DEPT, SALARY,
--
Nvl(Sum(SALARY) OVER(Partition By DEPT Order By DEPT, HIRE_DATE, ID ROWS BETWEEN 1 Following And Unbounded Following), 0) "SUM_SAL_LATER",
Nvl(Sum(1) OVER(Partition By DEPT Order By DEPT, HIRE_DATE, ID ROWS BETWEEN 1 Following And Unbounded Following), 0) "COUNT_EMP_LATER",
--
Nvl(Sum(SALARY) OVER(Partition By DEPT Order By DEPT, HIRE_DATE, ID ROWS BETWEEN 1 Following And Unbounded Following) /
Sum(1) OVER(Partition By DEPT Order By DEPT, HIRE_DATE, ID ROWS BETWEEN 1 Following And Unbounded Following), 0) "AVG_LATER",
--
SALARY -
Nvl((
Sum(SALARY) OVER(Partition By DEPT Order By DEPT, HIRE_DATE, ID ROWS BETWEEN 1 Following And Unbounded Following) /
Sum(1) OVER(Partition By DEPT Order By DEPT, HIRE_DATE, ID ROWS BETWEEN 1 Following And Unbounded Following)
), 0) "GAP"
from
emp
Order By
DEPT, HIRE_DATE, ID
) g
Order By
RN
)
CTE grid resultiing dataset:
RN
RN_DEPT
ID
EMP_NAME
HIRE_DATE
DEPT
SALARY
SUM_SAL_LATER
COUNT_EMP_LATER
AVG_LATER
GAP
DEPT_MAX_GAP
1
1
601
HILLER
23-JAN-82
60
4800
24000
4
6000
-1200
6000
2
2
602
MILLER
23-FEB-82
60
9000
15000
3
5000
4000
6000
3
3
603
SMITH
23-MAR-82
60
4800
10200
2
5100
-300
6000
4
4
604
FORD
23-APR-82
60
4200
6000
1
6000
-1800
6000
5
5
605
KING
23-MAY-82
60
6000
0
0
0
6000
6000
6
1
201
SCOT
23-MAR-82
20
13000
6000
1
6000
7000
7000
7
2
202
JONES
23-AUG-82
20
6000
0
0
0
6000
7000
Main SQL
SELECT
g.ID, g.EMP_NAME, g.HIRE_DATE, g.DEPT, g.SALARY, g.GAP
FROM
grid g
WHERE
g.GAP = g.DEPT_MAX_GAP
Order By
RN
Resulting as:
ID
EMP_NAME
HIRE_DATE
DEPT
SALARY
GAP
605
KING
23-MAY-82
60
6000
6000
201
SCOT
23-MAR-82
20
13000
7000
Without CTE and with all unnecessery columns excluded it looks like this:
SELECT ID, EMP_NAME, HIRE_DATE, DEPT, SALARY, GAP
FROM
(
( Select g.*, Max(GAP) OVER(PARTITION BY DEPT) "DEPT_MAX_GAP"
From( Select
ID, EMP_NAME, HIRE_DATE, DEPT, SALARY,
SALARY -
Nvl(( Sum(SALARY) OVER(Partition By DEPT Order By DEPT, HIRE_DATE, ID ROWS BETWEEN 1 Following And Unbounded Following) /
Sum(1) OVER(Partition By DEPT Order By DEPT, HIRE_DATE, ID ROWS BETWEEN 1 Following And Unbounded Following)
), 0) "GAP"
From emp
Order By DEPT, HIRE_DATE, ID
) g
)
)
WHERE GAP = DEPT_MAX_GAP
Order By DEPT, HIRE_DATE, ID
It seems like this is all you need.
Regards...

How use select * with group by subquery in sql server

I have table of employees salary details records with columns
Id Name Year Month Salary
1 ABC 2021 Jan 50000
2 PQR 2021 Jan 40000
3 KLM 2021 Feb 45000
4 LMN 2021 Jan 55000
5 LMN 2022 Jan 20000
6 ABC 2022 Feb 25000
7 ABC 2022 Jan 2500
8 ABC 2022 Dec 60000
9 LMN 2022 Nov 70000
Now I want to find which employee gets salary greater than 100000 from joining, and display employees all data
--find which employee gets more than 100000 salary till now
select name,sum(salary) as AnnualSalary from tblEmpsalary
group by Name
having sum(Salary)>100000 --this query works
--but below query display no data , (I want to show all data of employee which gets more than 100000 total salary)
SELECT id, name,Month,Year, SUM(Salary) AS TotalSales
FROM tblEmpsalary
GROUP BY name,Id,Month,Year,Salary
having SUM(Salary)>100000;
SELECT T.ID,T.Name,T.Year,T.Month,T.Salary
FROM tblEmpsalary AS T
JOIN
(
select ID
from tblEmpsalary
group by ID
having sum(Salary)>100000
)AS X ON T.ID=X.ID
You can use a window function for this
SELECT
id,
name,
Month,
Year,
TotalSales
FROM (
SELECT *,
SUM(Salary) OVER (PARTITION BY name) AS TotalSales
FROM tblEmpsalary e
) e
WHERE e.TotalSales > 100000;
Please, try with below query where one query for grouping and another is joining for fetch employees details:
SELECT TS.id, TS.name, TS.Month,Year, TS.Salary, ATS.TotalSales FROM
(SELECT Month, Year, SUM(Salary) AS TotalSales
FROM tblEmpsalary
GROUP BY Month,Year,Salary
HAVING SUM(Salary)>100000
) AS ATS
LEFT OUTER JOIN tblEmpsalary TS on ATS.Month = TS.Month and ATS.Year = TS.Year
ORDER BY TS.name, TS.Id, TS.Month, TS.Year, ATS.TotalSales

Running count for each 2 rows

I am trying to calculate running count for each 2 rows like below,
CREATE TABLE sales
(
EmpId INT,
Yr INT,
Sales DECIMAL(8,2)
)
INSERT INTO sales (EmpId, Yr, Sales)
VALUES (1, 2005, 12000), (1, 2006, 18000), (1, 2007, 25000),
(1, 2008, 25000), (1, 2009, 25000),
(2, 2005, 15000), (2, 2006, 6000), (2, 2007, 6000)
SELECT
EmpId, Yr, sales,
SUM(Sales) OVER (PARTITION BY empid ORDER BY empid ROWS BETWEEN 2 PRECEDING AND CURRENT ROW ) AS TotalSales2
FROM
sales
Output:
EmpId Yr sales TotalSales2
-----------------------------------
1 2005 12000 12000
1 2006 18000 30000
1 2007 25000 55000
1 2008 25000 68000
1 2009 25000 75000
2 2005 15000 15000
2 2006 6000 21000
2 2007 6000 27000
But expected output:
EmpId Yr Sales TotalSales2
-----------------------------------
1 2005 12000 12000
1 2006 18000 30000
1 2007 25000 25000
1 2008 25000 50000
1 2009 25000 25000
2 2005 15000 15000
2 2006 6000 21000
2 2007 6000 6000
What am I doing wrong in this query?
Note: SQL Servre version is 2012.
SELECT EmpId, Yr, Sales,
CASE WHEN ROW_NUMBER() OVER (PARTITION BY EmpId ORDER BY yr) % 2 = 0
THEN sales + lag(sales, 1, 0) OVER (PARTITION BY empid ORDER BY yr)
ELSE sales
END AS TotalSales2
FROM sales
Lag returns the previous row's value - when row_number() is even, add the current row's value to the previous row - otherwise, just show the sales for the current row. Partition each by EmpId, order each by yr - output matches the expected.
Also, thanks so much for adding the DDL/sample data.
The expression:
SUM(Sales) OVER (PARTITION BY empid
ORDER BY empid
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
calculates the sum considering the current row and the 2 rows immediately preceding it. So it actually calculates a rolling sum, which is what you really don't want.
I think you are actually looking for something like the following:
;WITH CTE_Group AS (
SELECT EmpId, Yr, sales,
(ROW_NUMBER() OVER (PARTITION BY empid ORDER BY yr) + 1 ) / 2 AS grp
FROM sales
)
SELECT EmpId, Yr, sales,
SUM(sales) OVER (PARTITION BY empid, grp
ORDER BY yr) AS TotalSales2
FROM CTE_Group
The above query uses a CTE in order to calculate field grp: the value of this field is 1 for the first two records of an empid partition, 2 for the next two records, and so on.
Using grp we can calculate the running total of sales for groups of 2 as is the requirement of the OP.
Demo here
Edit:
To offset a larger group of records try using (credit goes to #Max Szczurek for pointing this out):
(ROW_NUMBER() OVER (PARTITION BY empid ORDER BY yr) - 1 ) / n AS grp
where n is the number of records each group contains.
Although answer is already accepted, consider below query also. This will give the required output :
DECLARE #sales TABLE(EmpId INT, Yr INT, Sales DECIMAL(8,2))
INSERT INTO #sales ( EmpId, Yr, Sales )
VALUES (1, 2005, 12000),
(1, 2006, 18000),
(1, 2007, 25000),
(1, 2008, 25000),
(1, 2009, 25000),
(2, 2005, 15000),
(2, 2006, 6000),
(2, 2007, 6000)
;WITH SAMPLE_DATA
AS
(
SELECT ROW_NUMBER()over(partition by empid order by (select 100))SNO,* FROM #Sales
)
SELECT EmpId,Yr,Sales
,CASE WHEN (SNO%2=0)
THEN SALES+
(
SELECT Sales FROM SAMPLE_DATA T2 WHERE T2.EmpId=T1.EmpId AND T2.SNO=T1.SNO-1
)
ELSE Sales END
TotalSales2
FROM SAMPLE_DATA T1
OUTPUT
--------------------------------------
--EmpId Yr Sales TotalSales2
--------------------------------------
1 2005 12000.00 12000.00
1 2006 18000.00 30000.00
1 2007 25000.00 25000.00
1 2008 25000.00 50000.00
1 2009 25000.00 25000.00
2 2005 15000.00 15000.00
2 2006 6000.00 21000.00
2 2007 6000.00 6000.00
--------------------------------------

Oracle SQL Query:Find out which year total sales amount is maximum

my working table, Table name: sales
Here Is MY TABLE, [sl_no is primary key] table structure:
CREATE TABLE SALES
( SL_NO NUMBER PRIMARY KEY, REGION VARCHAR2(10) NOT NULL,
MONTH VARCHAR2(20) NOT NULL, YEAR NUMBER NOT NULL,
SALES_AMOUNT NUMBER NOT NULL )
and here is table data:
SQL> select * from sales;
SL_NO REGION MONTH YEAR SALES_AMOUNT
---------- ---------- -------------------- ---------- ------------
1 east december 2011 750000
2 east august 2011 800000
3 west january 2012 640000
5 east march 2012 1200000
6 west february 2011 580000
4 west april 2011 555000
6 rows selected.
I have tried this query to view total sales amount of those[2011,2012] year;
SELECT year, SUM(sales_amount) FROM sales GROUP BY year;
YEAR SUM(SALES_AMOUNT)
---------- -----------------
2011 2685000
2012 1840000
MY GOAL:> I want to find out the year of maximum sales amount.
I tried this,and work perfectly...but when i want to display that year also, it gives an Error.
SQL> select max(sum(sales_amount)) from sales group by year;
MAX(SUM(SALES_AMOUNT))
----------------------
2685000
SQL> select year, max(sum(sales_amount)) from sales group by year;
select year, max(sum(sales_amount)) from sales group by year
*
ERROR at line 1:
ORA-00937: not a single-group group function
Extra addition: if multiple rows have same value means....when sales amount of both year[2011,2012] remain same, Then....
plZ help me to Solve this problem.
This should work.
with yr_agg as (
select year, sum(sales_amount) as total
from sales
group by year
)
select year, total as max_total
from yr_agg
where total = (select max(total)
from yr_agg);
I think the simplest way is to order the results and take the first row:
select year, sales_amount
from (SELECT year, SUM(sales_amount) as sales_amount
FROM sales
GROUP BY year
order by sum(sales_amount) desc
) t
where rownum = 1;
EDIT:
If you need to display all the matching rows (which isn't mentioned in the question), I would suggest using the dense_rank() analytic function:
select year, sales_amount
from (SELECT year, SUM(sales_amount) as sales_amount,
dense_rank(over order by SUM(sales_amount) desc) as seqnum
FROM sales
GROUP BY year
order by sum(sales_amount) desc
) t
where seqnum = 1;
Or, you might like the max() version instead:
select year, sales_amount
from (SELECT year, SUM(sales_amount) as sales_amount,
max(sum(sales_amount)) over () as maxsa
FROM sales
GROUP BY year
order by sum(sales_amount) desc
) t
where sales_amount = maxsa;
Following select should do what you need (untested, do not have Oracle at home):
select year, total
from (
select year, sum(sales_amount) total
from sales
group by year
)
where total = (select max(total_amount)
from (
select year, sum(sales_amount) total_amount
from sales
group by year
))
Take in account, though, that it might give you different years in each execution if two of them have exactly the same total amount. You might want to include some more conditions to avoid this.
Here is my Query where multiple row can select
SELECT year,MAX(total_sale) as max_total
FROM
(SELECT year,SUM(sales_amount) AS total_sale FROM sales GROUP BY year)
GROUP BY
year HAVING MAX(total_sale) =
(SELECT MAX(total_sale) FROM (SELECT SUM(sales_amount) AS total_sale FROM sales GROUP BY year));

SQL server full join query

I have a full join sql query and i am retrieving the data from the same table.the problem is i am getting the null value where i am expecting the column name.
Example:
I am having a table where there are two columns typeOfPost,dob.
DOB TypeOfPost
--------- --------------
20/11/1998 Manager
1/1/2000 Sales
13/6/1999 Manager
20/1/1987 Manager
1/11/1985 Sales
Now when I am writing a join query like
select DATENAME(month,dob) as Red,count(TypeOfPost)
from tablename
where TypeOfPost='Manager'
group by DATENAME(month,dob) as A
full join
select DATENAME(month,dob) as Green,count(TypeOfPost)
from tablename
where TypeOfPost='Sales'
group by DATENAME(month,dob) as B on B.Green = A.Red
Output-- Expected Output--
--------------------- ---------------------
Month Man Sal Month Man Sal
-------- ----- ------ -------- ----- ------
January 1 1 January 1 1
NULL 1 NULL June 1 NULL
November 1 1 November 1 1
Now here the problem rise, I want 'June' in the column Month instead of NULL value.
So is there any way to get that??
Help me out.
Thanks.
One option is to
use a CASE statement in a subselect
Determine for given record if it is a manager or sales
substitute with 1 or 0 accordingly
SELECT and GROUP from this subselect the final results.
SQL Statement
SELECT Month
, SUM(Man) AS Man
, SUM(Sal) AS Sal
FROM (
SELECT DATENAME(MONTH, DOB) AS Month
, CASE WHEN TypeOfPost = 'Manager' THEN 1 ELSE 0 END AS Man
, CASE WHEN TypeOfPost = 'Sales' THEN 1 ELSE 0 END AS Sal
FROM tableName
) g
GROUP BY
Month
or
SELECT Month
, SUM(Man)
, SUM(Sal)
FROM (
SELECT DATENAME(MONTH, DOB) AS Month
, COUNT(TypeOfPost) AS Man
, 0 AS Sal
FROM tableName
WHERE TypeOfPost = 'Manager'
GROUP BY
DATENAME(MONTH, DOB)
UNION ALL
SELECT DATENAME(MONTH, DOB) AS Month
, 0 AS Man
, COUNT(TypeOfPost) AS Sal
FROM tableName
WHERE TypeOfPost = 'Sales'
GROUP BY
DATENAME(MONTH, DOB)
) g
GROUP BY
Month