My result set looks like:
ICount Owners ProgramCount
1344 AA 1
408 AA 2
185 AA 3
87 AA 4
54 AA 5
644 EA 1
81 EA 2
18 EA 3
3 EA 4
2 EA 5
4419 JA 1
1461 JA 2
700 JA 3
375 JA 4
209 JA 5
and I'd like the results to be:
Owner Total ProgramCount 1 ProgramCount 2 ProgramCount 3 ProgramCount 4 ProgramCount 5
AA 2078 1344 408 185 87 54
EA 748 644 81 18 3 2
JA 7164 4419 1461 700 375 209
where the Total column is the sum of ProgramCount 1 through 5.
How would I build my SQL to accomplish this?
A simple pivot in concert with a sum() over()
Example
Select *
From (
Select [Owners]
,Item = concat('Program Count ',[ProgramCount])
,[ICount]
,[Total] = sum([ICount]) over(partition by [Owners])
from YourTable
) src
Pivot (sum([ICount]) for [Item] in ([Program Count 1],[Program Count 2],[Program Count 3],[Program Count 4],[Program Count 5]) ) pvt
Order By [Owners]
Returns
select v.Owners as Owners,
sum(v.ICount) as Total,
sum(case when v.ProgramCount = 1 then v.ICount else 0 end) as ProgramCount1,
sum(case when v.ProgramCount = 2 then v.ICount else 0 end) as ProgramCount2,
sum(case when v.ProgramCount = 3 then v.ICount else 0 end) as ProgramCount3,
sum(case when v.ProgramCount = 4 then v.ICount else 0 end) as ProgramCount4,
sum(case when v.ProgramCount = 5 then v.ICount else 0 end) as ProgramCount5
from (
QUERY_WITH_YOUR_RESULT_SET
) as v(ICount, Owners, ProgramCount)
group by v.Owners
According to #Максим Золотенко's answer, You can use Conditional aggregation query with a group by like below
SELECT Owners,
SUM(ICount) AS Total,
SUM(CASE WHEN ProgramCount = 1 THEN ICount ELSE 0 END) AS ProgramCount_1,
SUM(CASE WHEN ProgramCount = 2 THEN ICount ELSE 0 END) AS ProgramCount_2,
SUM(CASE WHEN ProgramCount = 3 THEN ICount ELSE 0 END) AS ProgramCount_3,
SUM(CASE WHEN ProgramCount = 4 THEN ICount ELSE 0 END) AS ProgramCount_4,
SUM(CASE WHEN ProgramCount = 5 THEN ICount ELSE 0 END) AS ProgramCount_5
FROM TempTable
GROUP BY Owners
Live demo on db<>fiddle
Output
I have a table as below
Name Grade SubjectAorB
Pooja B 1
Preeti C 0
Preeti C 1
Chintu A 1
Deepika B 0
Deepika B 1
Peter A 0
John A 0
Last column SubjectAorB has values as 0 and 1. 0 means Subject A and 1 means Subject B. A student can have either of the subject or both. I want output as below:
Name Grade Subject A Subject B
Pooja B 0 1
Preeti C 1 1
Chintu A 0 1
Deepika B 1 1
Peter A 1 0
John A 1 0
You can do this using conditional aggregation:
SELECT
Name,
Grade,
SubjectA = MAX(CASE WHEN SubjectAorB = 0 THEN 1 ELSE 0 END),
SubjectB = MAX(CASE WHEN SubjectAorB = 1 THEN 1 ELSE 0 END)
FROM #tbl
GROUP BY Name, Grade
Data in my table looks like this
PAY_END_DT Sal
10/27/2013 0
11/10/2013 0
11/24/2013 2473.14
12/08/2013 0
01/19/2014 0
02/02/2014 0
02/16/2014 0
My desired result should be like as below
10/27/2013 11/10/2013
12/08/2013 02/16/2014
I need a SQL to generate this result set.. please help
SELECT
employee_id,
MIN(pay_end_dt) AS island_min_pay_end_dt,
MAX(pay_end_dt) AS island_max_pay_end_dt
FROM
(
SELECT
pay_end_dt,
ROW_NUMBER() OVER (PARTITION BY employee_id,
ORDER BY pay_end_dt ) AS full_set_ordinal,
ROW_NUMBER() OVER (PARTITION BY employee_id, sal
ORDER BY pay_end_dt ) AS zero_set_ordinal
FROM
yourTable
)
AS sorted_set
WHERE
sal = 0
GROUP BY
employee_id,
full_set_ordinal - zero_set_ordinal
;
Using your data as an example:
PAY_END_DT Sal FULL_SET_ORIDINAL ZERO_SET_ORDINAL "FULL - ZERO"
10/27/2013 0 1 1 0
11/10/2013 0 2 2 0
11/24/2013 2473.14 3 1 2
12/08/2013 0 4 3 1
01/19/2014 0 5 4 1
02/02/2014 0 6 5 1
02/16/2014 0 7 6 1
Which then allows us to include only the rows WHERE sal = 0 and then GROUP BY "FULL - ZERO" to get our two sets, and then finally apply the MIN() and MAX() functions.
As stated in a comment, this is known as "Gaps and Islands".
1 1 1 1
0 0 0 0 0
1 2 3 4 5 6 7 8 9 - ordinal from the whole set
1 2 3 4 - ordinal from just the "islands"
1 2 3 4 5 - ordinal from just the "gaps"
2 2 2 3 - ordinal of the "islands" (whole_set_id - islands_id)
0 0 3 4 4 - ordinal of the "gaps" (whole_set_id - gaps_id)
;)
SELECT "10/27/2013 11/10/2013"
UNION
SELECT "12/08/2013 02/16/2014"
Sample data:
groupID CustomerID CustomerAddr work_date work_order
CA123 ABC12345 123 MAIN ST 2/1/2012 WORKNEW
CA123 ABC12345 123 MAIN ST 10/9/2012 ZZZ888
CA123 ABC12345 123 MAIN ST 3/9/2013 ZZZ131
WA999 ZZZ99909 451 EAST ST 1/13/2013 SY1234
WA999 ZZZ99909 451 EAST ST 4/15/2013 WORKOTHER
WA999 ZZZ99909 451 EAST ST 5/17/2013 SY1244
WA999 ZZZ99909 451 EAST ST 12/8/2013 SY1334
CA123 ABD54321 522 AVE A 4/21/2013 WW9999
For the given sample data, I need to count how many times records with the same groupID and CustomerID appear within 6, 12 and 18 month intervals. The count should be independent of other records with same groupID and customerID.
The output should look something like this:
groupID CustomerID work_date six_month_count twelve_month_count twenty_four_month_count
CA123 ABC12345 2/1/2012 0 1 2
CA123 ABC12345 10/9/2012 1 1 1
CA123 ABC12345 3/9/2013 0 0 0
WA999 ZZZ99909 1/13/2013 2 3 3
WA999 ZZZ99909 4/15/2013 1 2 2
WA999 ZZZ99909 5/17/2013 0 1 1
WA999 ZZZ99909 12/8/2013 0 0 0
CA123 ABD54321 4/21/2013 0 0 0
The only thing I can think to do is to grab the current record and max records for each row, but i don't know how to count the records for the given ranges.
Any help would be appreciated.
You can join the table to itself, and then work out the 6,12 and 24 columns via an aggregate. I've used Work_Order as a key to avoid counting the record vs itself during the self join.
WITH cteCountInRange
AS
(
SELECT c1.CustomerID,
c1.GroupID,
c1.Work_Date as WorkDate1,
c2.Work_Date as WorkDate2,
CASE WHEN c2.Work_Date > c1.Work_Date AND c1.Work_Order <> c2.Work_Order
THEN 1
ELSE 0
END AS Eligible,
DATEDIFF(M, c1.Work_Date, c2.Work_Date ) AS MonthsDiff
FROM CustomerData c1 INNER JOIN CustomerData c2
ON c1.CustomerID = c2.CustomerID AND c1.GroupID = c2.GroupID
)
SELECT cir.CustomerID, cir.GroupID, cir.WorkDate1,
SUM(CASE WHEN Eligible = 1 AND MonthsDiff <= 6 THEN 1 ELSE 0 END) AS SixMonthCount,
SUM(CASE WHEN Eligible = 1 AND MonthsDiff <= 12 THEN 1 ELSE 0 END) AS TwelveMonthCount,
SUM(CASE WHEN Eligible = 1 AND MonthsDiff <= 24 THEN 1 ELSE 0 END) AS TwentyFourMonthCount
FROM cteCountInRange cir
GROUP BY cir.CustomerID, cir.GroupID, cir.WorkDate1;
Fiddle here
Edit
Additional comment - the reason we can't filter out "InEligible" rows in the CTE is because a line of ZEROES is needed if there are no other matched rows after the current one. If we filtered these, we would need another LEFT JOIN back to the original table to include the zero rows.
I have the first 4 columns of data, and I wan't to use the Ranking functions in the SQL 2008 R2 to derive the fifth column. What's the best way to partition the data into subgroups based on the nextiteminsubgroup and previousiteminsubgroup fields?
Group OrderInGroup NextItemInSubGroup PreviousItemInSubGroup SubGroup
1 1 1 0 1
1 2 1 1 1
1 3 1 1 1
1 4 0 1 1
1 5 0 0 2
1 6 0 0 3
1 7 1 0 4
1 8 1 1 4
1 9 0 1 4
2 1 0 0 1
2 2 0 0 2
2 3 0 0 3
2 4 1 0 4
2 5 0 1 4
3 1 0 0 1
4 1 0 0 1
4 2 0 0 2
4 3 0 0 3
A recursive CTE solution:
DECLARE #t TABLE
([Group] INT
,OrderInGroup INT
,NextItemInSubGroup INT
,PreviousItemInSubGroup INT
,SubGroup INT
)
INSERT #t
VALUES
(1,1,1,0,1),(1,2,1,1,1),(1,3,1,1,1),(1,4,0,1,1),(1,5,0,0,2),(1,6,0,0,3),
(1,7,1,0,4),(1,8,1,1,4),(1,9,0,1,4),(2,1,0,0,1),(2,2,0,0,2),(2,3,0,0,3),
(2,4,1,0,4),(2,5,0,1,4),(3,1,0,0,1),(4,1,0,0,1),(4,2,0,0,2),(4,3,0,0,3)
;WITH recCTE
AS
(
SELECT [Group], OrderInGroup,NextItemInSubGroup , PreviousItemInSubGroup, 1 AS subgroup
FROM #t
WHERE OrderInGroup = 1
UNION ALL
SELECT r.[Group], t.OrderInGroup,t.NextItemInSubGroup , t.PreviousItemInSubGroup,
CASE WHEN r.NextItemInSubGroup = 1 THEN r.subgroup ELSE r.subgroup + 1 END
FROM recCTE AS r
JOIN #t AS t
ON t.[Group] = r.[Group]
AND t.OrderInGroup = r.OrderInGroup + 1
)
SELECT * FROM recCTE
ORDER BY [Group],OrderInGroup ;
P.S. it's best practice to avoid using SQL keywords (e.g. GROUP) as table/column names
Seems like 0 and 0 restart the ranking.
Select
Rank() Over (
Partition By
[Group]
, Case When [NextItemInSubGroup] + [PreviousItemInSubGroup] = 0
Then 0
Else 1
End
Order By [OrderInGroup]
) as [SubGroup]
From Your_Table;