Group by in PIVOT operator - sql

How do i use the group by clause in the PIVOT operator?
I tried with the following code but i get the null values and the results are not getting aggregated.
select EmpName, CHN,HYD FROM location
PIVOT (Sum(salary) for EmpLoc in ([CHN], [HYD]))
AS
pivottable
I want the final output to be like this.
CHN HYD
kunder 400 200
shetty 150 150
or
CHN HYD Total
kunder 400 200 600
shetty 150 150 300
Total 550 350 900

Just add the derived column Total=CHN+HYD and a sub-query to create the Total Row
The Seq (though not displayed) will put the Total row at the bottom
Declare #YourTable table (EmpLoc varchar(25),EmpName varchar(25),Salary int)
Insert Into #YourTable values
('HYD','kunder',200)
,('HYD','shetty',150)
,('CHN','shetty',150)
,('CHN','kunder',200)
,('CHN','kunder',200)
Select EmpName, CHN,HYD,Total=CHN+HYD
From (
Select Seq=0,EmpLoc,EmpName,Salary From #YourTable
Union All
Select Seq=1,EmpLoc,'Total',Salary From #YourTable
) A
pivot (sum(Salary) for EmpLoc in ([CHN], [HYD])) P
Returns
EmpName CHN HYD Total
kunder 400 200 600
shetty 150 150 300
Total 550 350 900

Declare #YourTable table (EmpLoc varchar(25),EmpName varchar(25),Salary int)
Insert Into #YourTable values
('HYD','kunder',200)
,('HYD','shetty',150)
,('CHN','shetty',150)
,('CHN','kunder',200)
,('CHN','kunder',200)
;with cte as
(
SELECT * from
(
select * from #YourTable
) as y
pivot
(
sum(salary)
for EmpLoc in ([CHN], [HYD])
) as p
)
SELECT
EmpName,sum(CHN)CHN ,sum(HYD)HYD
FROM CTE
GROUP BY EmpName;

I have no issue using your code from your example to get your desired results. I am guessing that your query is not as simple as your example, and as such is introducing other complications not shown here.
You may need to use a subquery and pivot using just the columns necessary for the pivot and join back to the rest of your query to get the results you are looking for using pivot().
Using conditional aggregation may be a simpler solution:
select
empname
, CHN = sum(case when emploc = 'CHN' then salary else 0 end)
, HYD = sum(case when emploc = 'HYD' then salary else 0 end)
--, Total = sum(salary) /* Optional total */
from location
group by empname
rextester demo: http://rextester.com/LYRH81756
returns:
+---------+-----+-----+
| EmpName | CHN | HYD |
+---------+-----+-----+
| kunder | 400 | 200 |
| shetty | 150 | 150 |
+---------+-----+-----+

Related

Get the total Count and Sum of total Amount from down line record using SQL Server CTE

How can we Count and Sum all down-line rows to their up-line using SQL.
Current data:
ST_ID UPLINE AMOUNT
---------------------------
44930 52001 400
52016 52001 300
52001 9024 432
76985 9024 100
12123 35119 234
12642 35119 213
12332 23141 654
Here in above table, uplinedata 52001 two ST_ID with amount 400 and 300 each with total sum of 700 and ST_ID has 52001 as well with Amount 400, so total amount for 5201 will be 400 + 300 + 432 = 1132 and again upline 9024 has ST_ID of 52001 with 432 + 700 with total of 1132.
Expected Output:
UPLINE AMOUNT CNT
------------------------
52001 1132 2 (400 +300 + 432 | 1+1+1)
9024 1232 4 (700 + 432 + 100 | 2+1+1 = 4)
35119 447 2 (234 + 213 | 1+1 = 2)
23141 654 1
I thought of recursive CTE but could not able to gather the logic. Do anyone have any idea to achieve this. I am using SQL Server 2016.
As I understood, the Upline column is connected to ST_ID column, and you want to find the sum and count grouped by (Upline + all the matched values from ST_ID). i.e. Upline = 9024 is connected to ST_ID = 52001, so the sum for Upline = 9024 will be (432 + 100 from 9024 plus 300 + 400 from 52001).
You could use a recursive CTE as the following:
With CTE As
(
Select ST_ID, Upline, Amount From table_name
Union All
Select T.ST_ID, T.Upline, C.Amount
From table_name T Join CTE C
On C.Upline = T.ST_ID
)
Select Upline,
Sum(Amount) As Amount,
Count(*) As Cnt
From CTE
Group By Upline
See a demo.
Update according to the new requirement (in addition to the sum of the the previous query add the sum of values where ST_ID=Upline):
With CTE As
(
Select * From table_name
Union All
Select T.ST_ID, T.Upline, C.AMOUNT
From table_name T Join CTE C
On C.Upline = T.ST_ID
)
Select C.Upline,
Sum(C.Amount) + ISNULL(Sum(Distinct T.Amount), 0) As Amount,
Count(*) + Count(Distinct T.Amount) As Cnt
From CTE C Left Join table_name T
On C.Upline = T.ST_ID
Group By C.Upline
See demo.

How do you pivot some columns into rows using sql or t-sql

Here is the table
Income|Expenses|Other|Department|Date
2k | 4k| .5k|Marketing |2014-05-28
52k | 7k| .1k|Sales |2014-04-01
Result
Description|Value|Department|Date
Income | 2k|Marketing |2014-05-28
Expenses | 4k|Marketing |2014-05-28
Other | .5k|Marketing |2014-05-28
Income | 52k|Sales |2014-04-01
Expenses | 7k|Sales |2014-04-01
Other | .1k|Sales |2014-04-01
Actually, I can use SELECT + UNION to get the result but the requirement is to use Pivot.
How to get this result using PIVOT function?
You can unpivot your values via a Cross Apply
Select B.*
,A.Department
,A.Date
From YourTable A
Cross Apply ( values ('Income' ,Income )
,('Expences',Expenses)
,('Other' ,Other )
) B(Description,Value)
you can use UNPIVOT
DECLARE #SampleData TABLE (Income VARCHAR(20), Expenses VARCHAR(20), Other VARCHAR(20), Department VARCHAR(20), Date [Date])
INSERT INTO #SampleData VALUES
('2k ', '4k','.5k','Marketing','2014-05-28'),
('52k', '7k','.1k','Sales ','2014-04-01')
SELECT Description, Value, Department, Date
FROM #SampleData T
UNPIVOT ([Value] FOR [Description] IN ([Income], [Expenses], [Other])) AS UNPVT
Result:
Description Value Department Date
----------------- -------------------- -------------------- ----------
Income 2k Marketing 2014-05-28
Expenses 4k Marketing 2014-05-28
Other .5k Marketing 2014-05-28
Income 52k Sales 2014-04-01
Expenses 7k Sales 2014-04-01
Other .1k Sales 2014-04-01
You can use apply :
select tt.Description, tt.Value, t.Department, t.Date
from table t cross apply
( values ('Income', Income), ('Expenses', Expenses), ('Other', Other)
) tt(Description, Value);
Just for fun, here is a little technique to "dynamically" unpivot your data without Dynamic SQL or declaring all the columns.
Example
Select Description = B.[Key]
,B.Value
,A.Department
,A.Date
From YourTable A
Cross Apply ( Select [Key]
,Value
From OpenJson( ( Select A.* for JSON Path,Without_Array_Wrapper) )
Where [Key] not in ('Department','Date')
) B
Returns
Description Value Department Date
Income 2k Marketing 2014-05-28
Expenses 4k Marketing 2014-05-28
Other .5k Marketing 2014-05-28
Income 52k Sales 2014-04-01
Expenses 7k Sales 2014-04-01
Other .1k Sales 2014-04-01

I need to construct table like below, dynamically using pivot or unpivot

EmpSalary table:
EmpCode BASIC HRA CONV
--------------------------
1 10000 500 300
2 10000 500 300
Desired output:
SalaryCode SalaryDetails
EmpCode 1
BASIC 10000
HRA 500
CONV 300
Total 10800
EmpCode 2
BASIC 10000
HRA 500
CONV 300
Total 10800
Actually we need what DBMS are you using.
The following code that includes unpivot clause works for Sql-Server or Oracle :
select SalaryCode, SalaryDetails
from
(select EmpCode, BASIC,HRA,CONV,
(BASIC+HRA+CONV) sub_Total
from EmpSalary
) p
unpivot
(SalaryDetails for SalaryCode in
(EmpCode,BASIC,HRA,CONV,sub_Total)
) unpvt;
Rextester Demo

SQL Server Special display last row

I have this database Student.
ID ( auto incremment) Nume Sum DataEnter
1 Alex 100 2013-05-20
2 Marian 200 2014-04-14
3 Vasile 150 2012-01-01
4 Andrei 100 2013-05-04
5 Teo 200 2013-02-11
I use this to display duplicate :
SELECT ID, Nume, Sum, DataEnter, FROM dbo.student
Where ISNULL(Sum, 0) IN (Select ISNULL (Sum,0) from dbo.student
group BY SUM
HAVING COUNT(*)>1 )
order by SUM desc, ID desc, DataEnter desc
Display this :
200 5 Teo 2013-02-11
200 2 Marian 2014-04-14
100 4 Andrei 2013-05-04
100 1 Alex 2013-05-20
Now : I enter a new data for example :
100 6 Costy with today dataenter day : 2014-05-27
Display this :
200 5 Teo 2013-02-11
200 2 Marian 2014-04-14
100 6 Costy 2014-05-27
100 4 Andrei 2013-05-04
100 1 Alex 2013-05-20
I want to display last duplicate category that enter sort by date. I want to display :
100 6 Costy 2014-05-27
100 4 Andrei 2013-05-04
100 1 Alex 2013-05-20
200 5 Teo 2013-02-11
200 2 Marian 2014-04-14
because 100 7 Costy 2014-05-27 is the last enter.
I hope this will help you.
Set Dateformat ymd
Declare #Student table
(ID int identity(1,1), Name varchar(20),[Sum] int,DataEnter Date)
Insert into #Student Values
('Alex' ,100,'2013-05-20'),
('Marian' ,200,'2014-04-14'),
('Vasile' ,150,'2012-01-01'),
('Andrei' ,100,'2013-05-04'),
('Teo' ,200,'2013-02-11')
Select A.Sum,A.ID,A.Name,A.DataEnter
From #Student A
Join (
Select Sum [Sum],max(DataEnter) DataEnter
from #Student
group By Sum
having Count(*) > 1) Lu
On A.Sum = Lu.Sum
order By Lu.DataEnter Desc,A.ID Desc
Result
Adding record
Insert into #Student Values
('Costy' ,100,'2014-05-27')
Select A.Sum,A.ID,A.Name,A.DataEnter
From #Student A
Join (
Select Sum [Sum],max(DataEnter) DataEnter
from #Student
group By Sum
having Count(*) > 1) Lu
On A.Sum = Lu.Sum
order By Lu.DataEnter Desc,A.ID Desc
Result
Adding 1 more record
Insert into #Student Values
('Mary' ,200,'2015-05-27')
Select A.Sum,A.ID,A.Name,A.DataEnter
From #Student A
Join (
Select Sum [Sum],max(DataEnter) DataEnter
from #Student
group By Sum
having Count(*) > 1) Lu
On A.Sum = Lu.Sum
order By Lu.DataEnter Desc,A.ID Desc
result.

Problems with group by and order by

Hi I am a newbie to the world of sql but struggling to get some of the basics to work.
I have a set of data that looks like this:
Table name: Sample
PROJECT WORK ORDER AMOUNT
-----------------------------------------
111 a 100
222 b 200
111 c 300
444 d 400
111 e 500
666 f 600
I want it to end up looking like this:
Table name: Sample
PROJECT WORK ORDER AMOUNT PROJECT AMOUNT
--------------------------------------------------------
111 e 500 900
111 c 300 900
111 a 100 900
666 f 600 600
444 d 400 600
222 b 200 200
Sorted by project with the greatest TOTAL amount
Group by does not work for me as it groups all projects into one, so I can't see the 3 work order lines for "Project 111"
PROJECT WORK ORDER AMOUNT
-----------------------------------------
111 a 900
222 b 200
444 d 400
666 f 600
Order by does not work as I can't get it sort it out on the basis of the greatest project value
Table name: Sample
PROJECT WORK ORDER AMOUNT
-----------------------------------------
666 f 600
111 e 500
444 d 400
111 c 300
222 b 200
111 a 100
My alternative idea was if I could create another column "Project Amount" that calculates the projects total based on values in "Project" column and I can then easily sort it by Project Amount instead to achieve the desired format
Table name: Sample
PROJECT WORK ORDER AMOUNT PROJECT AMOUNT
--------------------------------------------------------
111 e 500 900
111 c 300 900
111 a 100 900
666 f 600 600
444 d 400 600
222 b 200 200
But I am struggling how to get column "Project Amount" to calculate all the projects total value and present them on any rows that appear with the same project number.
Any advise?
select *
, sum(amount) over (partition by project) as ProjAmount
, row_number() over
from YourTable
order by
ProjAmount desc
Example at SQL Fiddle.
To select only the top two projects with the highest amounts, you could use dense_rank:
select *
from (
select *
, dense_rank() over (order by ProjAmount desc) as dr
from (
select *
, sum(amount) over (partition by project) as ProjAmount
from YourTable
) WithProjAmount
) WithDenseRank
where dr < 3
order by
ProjAmount desc
Example at SQL Fiddle.
A version with plain SQL subquery
SELECT s.*,
(SELECT SUM(Amount) FROM Sample WHERE Project = s.Project) ProjectAmount
FROM Sample s
ORDER BY ProjectAmount DESC
SQLFiddle
SELECT a.project ,
a.work ,
a.amount ,
b.proj_amount
FROM project A
JOIN ( SELECT SUM(amount) proj_amount ,
project
FROM project
WHERE project = project
GROUP BY project
) b ON a.project = b.project
ORDER BY proj_amount DESC ,
amount DESC