SQL 2008 Using Top Avg an top row field - sql

With the results of Loc_1 query below, I would like to include the top row balance field 100.
select AVG(Age) as AvgAge , Balance as BalanceLeft from
(
select top(5)* from Employee
where LocationID=1
order by HireDate desc
) as Loc_1
Age HireDate LocationID Balance
30 10-12-2014 1 100
20 09-12-2014 1 200
40 08-12-2014 1 300
50 07-12-2014 1 400
20 06-12-2014 1 500
Results
AvgAge Balanceleft
32 100
i just want to include just the top row column balance i.e 100

You can use conditional aggregation, if I understand the question correctly:
select AVG(Age) as AvgAge,
max(case when seqnum = 1 then Balance end) as BalanceLeft
from (select top(5) e.*,
row_number() over (order by hiredate desc) as seqnum
from Employee e
where LocationID=1
order by HireDate desc
) Loc_1

Related

SQL Query to find percentage difference in salary by emp id

I have the following table
CREATE TABLE test1 (in_date date, emp_id int , salary int);
INSERT INTO test1 (in_date, emp_id, salary)
VALUES
('01-01-2020',1,4000),
('01-01-2020',2,3000),
('01-01-2020',3,6000),
('01-01-2020',4,9000),
('02-01-2020',1,2000),
('02-01-2020',2,5000),
('02-01-2020',3,4200),
('02-01-2020',4,8500);
In_date
emp_id
salary
01-01-2020
1
4000
01-01-2020
2
3000
01-01-2020
3
6000
01-01-2020
4
9000
02-01-2020
1
2000
02-01-2020
2
5000
02-01-2020
3
4200
02-01-2020
4
8500
I would like to calculate percentage change in salary per emp_id
The output should look like this
emp_id
per_diff_salary
1
50%
2
-67%
3
30%
4
6%
any help is highly appreciated
You need ROW_NUMBER to identify which row is the first and which the second. Then use conditional aggregation over that.
The calculation you are looking for appears to be 100 - SecondValue * 100 / FirstValue
SELECT
t1.emp_id,
[percent] = 100 - MIN(CASE WHEN t1.rn = 2 THEN t1.salary END) * 100.0 / MIN(CASE WHEN t1.rn = 1 THEN t1.salary END)
FROM (
SELECT *,
rn = ROW_NUMBER() OVER (PARTITION BY t1.emp_id ORDER BY t1.in_date)
FROM test1 t1
) t1
GROUP BY
t1.emp_id
db<>fiddle
If you could have more than two rows per emp_id then you need something to identify the last row
SELECT
t1.emp_id,
[percent] = 100 - MIN(CASE WHEN t1.NextDate IS NULL THEN t1.salary END) * 100.0 / MIN(CASE WHEN t1.rn = 1 THEN t1.salary END)
FROM (
SELECT *,
rn = ROW_NUMBER() OVER (PARTITION BY t1.emp_id ORDER BY t1.in_date),
NextDate = LEAD(t1.in_date) OVER (PARTITION BY t1.emp_id ORDER BY t1.in_date) -- will be null on the last row
FROM test1 t1
) t1
GROUP BY
t1.emp_id
You may subselect the max and min date and then choose those salaries to calculate by joining by date
select distinct e.emp_id,
( CAST((maxsal.salary-minsal.salary)*100 AS DECIMAL(10,2))/ CAST(minsal.salary AS DECIMAL(10,2)) ) as salary_increase_from_first_to_last_salary
FROM test1 E
join (
select emp_id, salary FROM test1 EE where in_date =
(select min(in_date) FROM test1 EEE where EEE.emp_id = EE.emp_id)
) as minsal on minsal.emp_id = e.emp_id
join (
select emp_id, salary FROM test1 EE where in_date =
(select max(in_date) FROM test1 EEE where EEE.emp_id = EE.emp_id)
) as maxsal on maxsal.emp_id = e.emp_id
;
You may need to adapt the casts, or you may not need them at all.
Of course, if the table is big this may not be the best way performancewise and you may need to analise its plan before you accept it. Probably you can find a best way using other subselects or grouping, but this is the most obvious way IMHO by pure SQL.

Sum SQL rows on every nth and group

My data is as follows:
QUARTER_CMT DEPT SALES
7 A 39
8 A 23
9 A 33
10 A 45
11 A 50
12 A 110
1 B 44
2 B 56
3 B 87
4 B 22
I am aiming at getting total SALES for every 4 quarters by DEPT and retain Quarter_CNT number as a rollup.
QUARTER_CMT DEPT SALES_TOT
7 A 140
11 A 160
1 B 209
Work in progress code. I thought to sub-group totals and join to latest QUARTER_CMT to have one row by DEPT :
SELECT
ROW_NUMBER() OVER(PARTITION BY DEPTORDER BY QUARTER_CMT) as RN,
QUARTER_CMT,
DEPT,
--RANK() OVER(PARTITION BY ODDZIAL ORDER BY QUARTER_COUNT) AS RowNumberRankOrg,
--NTILE(4) OVER(PARTITION BY ODDZIAL ORDER BY QUARTER_COUNT) AS RowNumberRank,
SALES
FROM TABLE_1
ORDER BY DEPT, QUARTER_CMT
Try this:
with a as (
select
row_number() over(partition by dept order by quarter_cmt)-1 n,
quarter_cmt, dept, sales
from t
order by dept, quarter_cmt
)
select min(quarter_cmt) quarter_cmt, dept, sum(sales) sales_tot
from a
group by dept, floor(n/4)
order by dept, quarter_cmt
;
Assuming this sample data:
create or replace table t as
select $1 QUARTER_CMT, $2 DEPT, $3 SALES from values
(7,'A',39),
(8,'A',23),
(9,'A',33),
(10,'A',45),
(11,'A',50),
(12,'A',110),
(1,'B',44),
(2,'B',56),
(3,'B',87),
(4,'B',22)
;
you need to divide row_number to 5 for determine the periods
SELECT MIN (QUARTER_CMT) QUARTER_CMT, DEPT, SUM(SALES) SALES, PART
FROM (
SELECT *, FLOOR(ROW_NUMBER() OVER(PARTITION BY DEPT ORDER BY QUARTER_CMT) / 5) AS PART
FROM TABLE_1
) AS T
GROUP BY DEPT, PART
ORDER BY DEPT, PART
Result:
QUARTER_CMT DEPT SALES PART
----------- ---- ----------- -----------
7 A 140 0
11 A 160 1
1 B 209 0

Find the 3rd Maximum Salary for each department based on table data

I need to find out the 3rd maximum salary for an employee for each department in a table. if no 3rd maximum salary exists then display 2nd maximum salary. if no 2nd maximum salary exist then find the highest salary. How to achieve this result in sql-server?
The table structure is given below
create table employee1(empid int, empname varchar(10), deptid int, salary money)
insert into employee1
select 1,'a',1, 1000
union
select 1,'b',1, 1200
union
select 1,'c',1, 1500
union
select 1,'c',1, 15700
union
select 1,'d',2, 1000
union
select 1,'e',2, 1200
union
select 1,'g',3, 1500
I have tried the common way of getting the maximum salary for each category using row_number function.
;with cte
as
(
select ROW_NUMBER( ) over( partition by deptid order by salary) as id, * from employee1
)
select * from cte
Select EmpID,empname,deptid,salary
From (
Select *
,RN = Row_Number() over (Partition By deptid Order By Salary)
,Cnt = sum(1) over (Partition By deptid)
From employee1
) A
Where RN = case when Cnt<3 then Cnt else 3 end
Returns
The answer will depend if you want ties and how to handle them. If you want no ties and even if one employee ties with another it becomes the next highest salary then the trick is to use a row_number like the one you are showing only with descending on salary and then use another row_number to reverse it. If you did not want to use row_number a second time you could do it with a few other techniques as well but step 1 is find highest step 2 is to reverse that order
; WITH cteRankSalariesByDepartment AS (
SELECT
*
,RowNum = DENSE_RANK() OVER (PARTITION BY deptid ORDER BY salary DESC)
FROM
employee1
)
, cteReverseRankHighestSalaries AS (
SELECT
*
,RowNum2 = DENSE_RANK() OVER (PARTITION BY deptid ORDER BY RowNum DESC)
FROM
cteRankSalariesByDepartment
WHERE
RowNum <= 3
)
SELECT *
FROM
cteReverseRankHighestSalaries
WHERE
RowNum2 = 1
Per your comment updated to DENSE_RANK() you could simply use it in place of row_number() and you will get your ties.
Just you query needs count and row_number with condition as below:
;with cte
as
(
select ROW_NUMBER( ) over( partition by deptid order by salary desc) as id,
Cnt = count(*) over(partition by deptid), * from employee1
)
select * from cte where ( cnt >= 3 and id = 3 )
or ( cnt < 3 and id = 1 )
you can try a query like below:
select * from
(
select
empid,
empname ,
deptid ,
salary ,
ROW_NUMBER( ) over( partition by deptid order by id desc) as rev_id
from
(
select
ROW_NUMBER( ) over( partition by deptid order by salary) as id,
empid,
empname ,
deptid ,
salary
from employee1
)
t where id<=3
)t where rev_id=1
working demo
You could use UNION
;with cte
as
(
select ROW_NUMBER( ) over( partition by deptid order by salary) as id, * from employee1
)
--get the 3rd highest
select
*
from cte
where id = 3
union
--get the highest / max
select
c.*
from cte c
--this determines the highest which salary for each dept
inner join
(select deptid, max(id) id
from cte
group by deptid) x on x.deptid = c.deptid and x.id = c.id
--this limits it on depts that aren't in the list in the first part of the query
where
c.deptid not in (select deptid from cte where id = 3)
To increase your question I added two employees with the same salary in the third position.
To get this you need the first dense_rank the salary by department. After you need to reverse l the salary rank and get the position 1
try it
DECLARE #employee1 TABLE
(
empid INT,
empname VARCHAR(10),
deptid INT,
salary MONEY
)
INSERT #employee1 Values
(1,'a',1, 1000 )
,(1,'b',1, 1200 )
,(2,'bb',1, 1200 )
,(1,'c',1, 1500 )
,(3,'ccc',1, 1500 )
,(1,'c',1, 15700)
,(1,'d',2, 1000 )
,(1,'e',2, 1200 )
,(1,'g',3, 1500 )
WITH cte_rank
AS (SELECT Dense_rank()
OVER (
partition BY deptid
ORDER BY salary) SalaryRank,
*
FROM #employee1),
cte_final
AS (SELECT Dense_rank()
OVER (
partition BY deptid
ORDER BY salaryrank DESC) SalaryRankReverse,
*
FROM cte_rank
WHERE salaryrank <= 3)
SELECT *
FROM cte_final
WHERE salaryrankreverse = 1
Result
SalaryRankReverse SalaryRank empid empname deptid salary
-------------------- -------------------- ----------- ---------- ----------- ---------------------
1 3 1 c 1 1500.00
1 3 3 ccc 1 1500.00
1 2 1 e 2 1200.00
1 1 1 g 3 1500.00

How to find sum of first three salary where total rows on the table six rows

How to find sum of first three salary where total rows on the table six rows.
id Salary
01 100
02 200
03 300
04 400
05 500
06 600
Try using this.....
SELECT SUM(salary)
FROM
(SELECT
ROW_NUMBER() OVER (ORDER BY id ASC) AS ROW_NUMBER, salary
FROM tablename) AS foo
WHERE
ROW_NUMBER <= 3
Do you mean this:
SELECT SUM(Salary)
FROM (
SELECT Salary FROM your_table ORDER BY id LIMIT 3
) A
Assuming it to be SQL Server:
SELECT SUM(Salary) As SalaryTotal
(
SELECT TOP 3 * FROM your_table ORDER BY ID
)
If you want SUM of highest 3 salaries then you can ORDER BY Salary:
SELECT SUM(Salary) As SalaryTotal
(
SELECT TOP 3 * FROM your_table ORDER BY salary DESC
)

Taking the Largest SUM from a table

I'm trying to get the Employee with the highest sales
Employee DeptNo Date Sales
Chris 2 2012/1/1 1000
Joe 1 2012/1/1 900
Arthur 3 2012/1/1 1100
Chris 2 2012/3/1 1200
Joe 1 2012/2/1 1500
Arthur 3 2010/2/1 1200
Joe 1 2010/3/1 900
Arthur 3 2010/3/1 1100
Arthur 3 2010/4/1 1200
Joe 1 2012/4/1 1500
Chris 2 2010/4/1 1800
I've tried using two subqueries, and then comparing them together to find the higher value
SELECT c1.Employee,
c1.TOTAL_SALES
FROM (SELECT Employee,
Sum(sales) AS TOTAL_SALES
FROM EmployeeSales
GROUP BY Employee) c1,
(SELECT Employee,
Sum(sales) AS TOTAL_SALES
FROM EmployeeSales
GROUP BY Employee) c2
WHERE ( c1.TOTAL_SALES > c2.TOTAL_SALES
AND c1.Employee > c2.Employee )
But the resulting query gives me two rows of
Employee TOTAL_SALES
joe 4800
joe 4800
What am I doing wrong?
I would use a CTE.
;With [CTE] as (
Select
[Employee]
,sum([Sales]) as [Total_Sales]
,Row_Number()
Over(order by sum([sales]) Desc) as [RN]
From [EmployeeSales]
Group by [Employee]
)
Select
[Employee]
,[Total_Sales]
From [CTE]
Where [RN] = 1
Example of working code SQL Fiddle:
http://sqlfiddle.com/#!3/bd772/2
To return all employees with the highest total sales, you can use SQL Server's proprietary TOP WITH TIES:
SELECT TOP (1) WITH TIES name, SUM(sales) as total_sales
FROM employees
GROUP BY name
ORDER BY SUM(sales) DESC
SELECT name, SUM(sales) as total_sales
FROM employees
GROUP BY name
ORDER by total_sales DESC
LIMIT 1;
A better solution is to group by an employee id so we are sure they are the same person. Since there can be two Chris's.
I would use a window partition
select * from
(
select
employee
, sum(sales) as sales
, row_number() over
(
order by sum(sales) desc
) as rank
from EmployeeSales
group by employee
) tmp
where tmp.rank = 1
And I agree with what someone said (Shawn) about having an employeeID and group by that for this, rather than the name.
(I removed the partition from the row_number() call as it is not needed for this)
you can use CTE for that
WITH CTE
AS ( select employee , sum(sales) as sales,
ROW_NUMBER() OVER (PARTITION BY employee ORDER BY sum(sales) desc) RN
FROM EmployeeSales)
SELECT employee ,
sales
FROM CTE
WHERE RN =1