Sum Based on Grouping and Order - sql-server

I have raw data similar to below ...
WO OP WC Time
1 10 Band Saw 2.0
1 15 Band Saw 5.0
1 17 Band Saw 10.0
1 20 CNC Lathe 6.0
1 22 Band Saw 102.0
1 30 Inspection 33.0
2 10 Band Saw 1.5
2 20 CNC Lathe 6.00
2 20 CNC Lathe 2.00
2 30 CNC Punch 0.5
2 40 Manual Ops 1.25
2 50 Inspection 0.00
I need it to Sum and Group like below. ie summing the Time for the WO/WC column until the WC changes, not a total for the overall WO. Hope I have explained that clearly enough (probably not)
We need to support SQL Server 2005.
WO WC Time
1 Band Saw 17.0
1 CNC Lathe 6.0
1 Band Saw 102.0
1 Inspection 33.0
2 Band Saw 1.5
2 CNC Lathe 8.00
2 CNC Punch 0.5
2 Manual Ops 1.25
2 Inspection 0.00

What about something like this. I build a row_number over the order you defined (WO and OP), Then, I build another row_number over each WC sorted by WO and OP. now you have two row_numbers. One over the entire set, and one over each WC. Now, when you subtract the latter from the former, you form groups wherever there is only 1 row difference, Each time there is MORe than one row, you get a new grouping. No recursion necessary.
;with t (WO, OP, WC, Time) as
(
select 1, 10, 'Band Saw', 2.0
union all select 1, 15, 'Band Saw', 5.0
union all select 1, 17, 'Band Saw', 10.0
union all select 1, 20, 'CNC Lathe', 6.0
union all select 1, 22, 'Band Saw', 102.0
union all select 1, 30, 'Inspection', 33.0
union all select 2, 10, 'Band Saw', 1.5
union all select 2, 20, 'CNC Lathe', 6.00
union all select 2, 20, 'CNC Lathe', 2.00
union all select 2, 30, 'CNC Punch', 0.5
union all select 2, 40, 'Manual Ops', 1.25
union all select 2, 50, 'Inspection', 0.00
), rn as
(
select
grp= row_number() over (order by WO, op) - row_number() over (partition by wo, wc order by wo, op),
*
from t
)
select grp, wo, wc, sum(time)
from rn
group by grp, wo, wc
Edited to use one cte. Also fixed the partition in the second order by. This outperforms an rcte with the data provided and will probably scale better because it doesnt have to recurse an arbitrary number of times.

Use a recursive CTE:
;WITH
cte1 AS
(
SELECT WO, OP, WC, Time,
ROW_NUMBER() OVER (ORDER BY WO, Op) AS RowNumber
FROM MyTable
),
cte2 AS
(
SELECT WO, OP, WC, Time, RowNumber,
1 AS GroupID
FROM cte1
WHERE RowNumber = 1
UNION ALL
SELECT cte1.WO, cte1.OP, cte1.WC, cte1.Time, cte1.RowNumber,
CASE
WHEN cte1.WC = cte2.WC THEN cte2.GroupID
ELSE cte2.GroupID + 1
END AS GroupID
FROM cte1
INNER JOIN cte2 ON cte1.RowNumber = cte2.RowNumber + 1
)
SELECT WO, WC, SUM(Time) As TotalTime
FROM cte2
GROUP BY GroupID, WO, WC
OPTION (MAXRECURSION 0)
I don't have SQL Server 2005 around to test but it should work. If you want more details, query cte1 and cte2.

Thanks to Xedni who supplied me with the bulk of the answer. I had to add the Max(op) and the sort to his supplied solution.
;with t (WO, OP, WC, Time) as
(
select 1, 10, 'Band Saw', 2.0
union all select 1, 15, 'Band Saw', 5.0
union all select 1, 17, 'Band Saw', 10.0
union all select 1, 20, 'CNC Lathe', 6.0
union all select 1, 22, 'Band Saw', 102.0
union all select 1, 30, 'Inspection', 33.0
union all select 2, 10, 'Band Saw', 1.5
union all select 2, 20, 'CNC Lathe', 6.00
union all select 2, 20, 'CNC Lathe', 2.00
union all select 2, 30, 'CNC Punch', 0.5
union all select 2, 40, 'Manual Ops', 1.25
union all select 2, 50, 'Inspection', 0.00
), rn as
(
select
grp= row_number() over (order by WO, op) - row_number() over (partition by wo, wc order by wo, op),
*
from t
)
select grp, wo, MAX(op) AS MaxOp, wc, sum(time)
from rn
group by grp, wo, wc
ORDER BY wo, MaxOp

Related

Uninterrupted occurrences of weekly events

declare #t table([CH_FMT_ID] nvarchar(3),[CH_ISS_DT] date,[CH_RANK] int,[CH_TTL_ID] bigint)
INSERT INTO #t values
('HSI', '6/15/2002', 28, 397130),
('HSI', '6/8/2002', 24, 397130),
('HSI', '6/1/2002', 23, 397130),
('HSI', '5/25/2002', 20, 397130),
('HSI', '5/18/2002', 13, 397130),
('HSI', '5/11/2002', 12, 397130),
('HSI', '5/4/2002', 11, 397130),
('HSI', '6/15/2002', 28 , 111111),
('HSI', '6/8/2002', 24 , 111111),
('HSI', '6/1/2002', 23 , 111111),
('HSI', '5/25/2002', 20 , 111111),
('HSI', '5/18/2002', 13 , 111111),
('HSI', '5/11/2011', 12 , 111111),
('HSI', '5/4/2011', 11 , 111111);
SELECT chart.CH_TTL_ID
,chart.[CH_FMT_ID] Chart
,min(chart.[CH_RANK]) Peak
,max(chart.[CH_RANK]) Trough
,count(chart.[CH_RANK]) Weeks
,MIN(chart.CH_ISS_DT) EntryDate
,MAX(chart.CH_ISS_DT) ExitDate
,(DATEDIFF(day, MIN(CH_ISS_DT),MAX(CH_ISS_DT)) / 7) Weeks_Charted
FROM #t chart
group by chart.CH_TTL_ID
,chart.[CH_FMT_ID]
order by Weeks_Charted desc
The CH_ISS_DT is a date which is updated 1x / week. So the dates are 2017-12-31,2017-12-24, 2017-12-17, etc. The problem comes with the Weeks_Charted column. If something happens the first time in 2007 and doesn't happen again until 2017, the Weeks_Charted will count all weeks in between, even when the data is not present all the time.
This sample returns:
CH_TTL_ID,Chart,Peak,Trough,Weeks,EntryDate,ExitDate,Weeks_Charted
111111,HSI,11,28,7,2002-05-18,2011-05-11,468
397130,HSI,11,28,7,2002-05-04,2002-06-15,6
I am indifferent if the CH_TTL_ID returns 2 rows, one with a 2 as weeks_charted and the other with a 5, or just returns the 2 for the most recent data and the max, mins are different.
Try this query. Query groups by continuous weeks.
select
CH_TTL_ID, CH_FMT_ID Chart
,min([CH_RANK]) Peak
,max([CH_RANK]) Trough
,count([CH_RANK]) Weeks
,MIN(CH_ISS_DT) EntryDate
,MAX(CH_ISS_DT) ExitDate
from (
select
*, sum(N) over (partition by CH_TTL_ID order by CH_ISS_DT) grp
from (
select
*, iif(datediff(dd, lag(CH_ISS_DT) over (partition by CH_TTL_ID order by CH_ISS_DT), CH_ISS_DT) = 7, 0, 1) N
from
#t chart
) t
) t
group by CH_TTL_ID, CH_FMT_ID, grp
One option is to use Recursive CTE in SQL 2008 for this kind of problems
;with cte as (
select
*,row_number() over (partition by CH_TTL_ID, CH_FMT_ID order by CH_ISS_DT) rn
from
#t chart
)
, rcte as (
select
*, 1 grp
from
cte
where
rn = 1
union all
select
a.CH_FMT_ID, b.CH_ISS_DT, b.CH_RANK, a.CH_TTL_ID, b.rn, a.grp + case when datediff(dd, a.CH_ISS_DT, b.CH_ISS_DT) = 7 then 0 else 1 end
from
rcte a
join cte b on a.CH_FMT_ID = b.CH_FMT_ID and a.CH_TTL_ID = b.CH_TTL_ID and a.rn + 1 = b.rn
)
select
CH_TTL_ID, CH_FMT_ID Chart
,min([CH_RANK]) Peak
,max([CH_RANK]) Trough
,count([CH_RANK]) Weeks
,MIN(CH_ISS_DT) EntryDate
,MAX(CH_ISS_DT) ExitDate
from
rcte
group by CH_TTL_ID, CH_FMT_ID, grp
order by CH_FMT_ID, CH_TTL_ID
option (maxrecursion 0)

Get contiguous date ranges grouped into a single line

I have a table which holds a lot of rows (Currently, 500K, expected to rise to 15 Million in the next 3 years). The table holds payments made for a certain event for a specific day. And event can have 1 or many payments for the same day, but the payments on the same day must have different PaymentTypes.
Below is a table variable creation that creates the basic data (Input), and then a hardcoded select which is the expected output. I need to group continuous date ranges for a payment type, for a placement, in one row, with a From and To date, and then when there's a break - no rows, and then for the next date range.
For example:
Placement 1 got payments from the 1st to the 2nd (2 days) for Payment type 5, and then from the 4th until the 6th for the same type. Therefore, two rows. Payment type 1 also got a payment for the 1st to the 3rd for payment type 10. So, that is another row.
DECLARE #Temp TABLE
(
Id INT NOT NULL IDENTITY(1,1),
PlacementId INT NOT NULL,
PaymentTypeId INT NOT NULL,
DateValue DATETIME NOT NULL,
Amount DECIMAL(16,2) NOT NULL
)
INSERT INTO #Temp (PlacementId, PaymentTypeId, DateValue, Amount)
SELECT 1, 5, '01-JAN-2015', 100 UNION
SELECT 1, 5, '02-JAN-2015', 150 UNION
SELECT 1, 5, '04-JAN-2015', 78 UNION
SELECT 1, 5, '05-JAN-2015', 89 UNION
SELECT 1, 5, '06-JAN-2015', 22 UNION
SELECT 1, 10, '01-JAN-2015', 10 UNION
SELECT 1, 10, '02-JAN-2015', 10 UNION
SELECT 1, 10, '03-JAN-2015', 15 UNION
SELECT 2, 5, '01-JAN-2015', 200 UNION
SELECT 2, 5, '02-JAN-2015', 5 UNION
SELECT 2, 5, '03-JAN-2015', 50 UNION
SELECT 3, 5, '01-JAN-2015', 80 UNION
SELECT 4, 5, '07-JAN-2015', 100 UNION
SELECT 4, 5, '08-JAN-2015', 12 UNION
SELECT 4, 5, '12-JAN-2015', 66 UNION
SELECT 4, 5, '14-JAN-2015', 4 UNION
SELECT 5, 10, '08-JAN-2015', 10
SELECT * FROM #Temp
SELECT 1 AS PlacementId, 5 AS PaymentTypeId, '2015-01-01' AS FromDate, '2015-01-02' AS ToDate, 250 AS Amount UNION
SELECT 1, 10, '2015-01-01', '2015-01-03', 35 UNION
SELECT 1, 5, '2015-01-04', '2015-01-06', 189 UNION
SELECT 2, 5, '2015-01-01', '2015-01-03', 255 UNION
SELECT 3, 5, '2015-01-01', '2015-01-01', 80 UNION
SELECT 4, 5, '2015-01-07', '2015-01-08', 112 UNION
SELECT 4, 5, '2015-01-12', '2015-01-12', 66 UNION
SELECT 4, 5, '2015-01-14', '2015-01-14', 4 UNION
SELECT 5, 10, '2015-01-08', '2015-01-08', 10
Note, there is a NCI on PlacementID and PaymentTypeID.
We're doing it at the moment with a crazy load of cursors, and are having extreme speed issues. (The 500K lines takes 4 minutes to process).
Is there an efficient method to achieve the desired output?
This is a problem called Grouping Islands of Contiguous Dates. Read this article be Jeff Moden for more info.
SQL Fiddle
;WITH Cte AS(
SELECT *,
RN = DATEADD(DAY, - ROW_NUMBER() OVER(PARTITION BY PlacementId, PaymentTypeId ORDER BY DateValue), DateValue)
FROM #Temp
)
SELECT
PlacementId,
PaymentTypeId,
FromDate = MIN(DateValue),
ToDate = MAX(DateValue),
Amount = SUM(Amount)
FROM Cte
GROUP BY PlacementId, PaymentTypeId, RN
ORDER BY PlacementId, PaymentTypeId, FromDate

Select rows based on every nth interval of time

I have a table with a primary key (bigint), datetime, value, foreignKey to configuration tabel that consists of 100,000's of rows. I want to be able to obtain a row for a variable time interval. For example.
Select Timestamp, value from myTable where configID=3
AND{most recent for 15 min interval}
I have a CTE query that returns multiple rows for the interval interval
WITH Time_Interval(timestamp, value, minutes)
AS
(
Select timestamp, value, DatePart(Minute, Timestamp) from myTable
Where Timestamp >= '12/01/2012' and Timestamp <= 'Jan 10, 2013' and
ConfigID = 435 and (DatePart(Minute, Timestamp) % 15) = 0
)
Select Timestamp, value, minutes from Time_Interval
group by minutes, value, timestamp
order by Timestamp
such as:
2012-12-19 18:15:22.040 6.98 15
2012-12-19 18:15:29.887 6.98 15
2012-12-19 18:15:33.480 7.02 15
2012-12-19 18:15:49.370 7.01 15
2012-12-19 18:30:41.920 6.95 30
2012-12-19 18:30:52.437 6.93 30
2012-12-19 19:15:18.467 7.13 15
2012-12-19 19:15:34.250 7.11 15
2012-12-19 19:15:49.813 7.12 15
But as can be seen there are 4 for the 1st 15 minute interval, 2 for the next interval, etc... Worse,
If no data was obtain at an exact times stamp of 15 minutes, then there will be no value.
What I want is the most recent value for a fifteen minute interval... if if the only data for that intervall occurred at 1 second after the start of the interval.
I was thinking of Lead/over but again... the rows are not orgainzed that way. Primary Key is a bigInt and is a clustered Index. Both the timstamp column and ConfigID columns are Indexed. The above query returns 4583 rows in under a second.
Thanks for any help.
Try this on for size. It will even handle returning one row for instances when you have multiple timestamps for a given interval.
NOTE: This assumes your Bigint PK column is named: idx. Just substitute where you see "idx" if it is not.
;WITH Interval_Helper([minute],minute_group)
AS
(
SELECT 0, 1 UNION SELECT 1, 1 UNION SELECT 2, 1 UNION SELECT 3, 1 UNION SELECT 4, 1
UNION SELECT 5, 1 UNION SELECT 6, 1 UNION SELECT 7, 1 UNION SELECT 8, 1 UNION SELECT 9, 1
UNION SELECT 10, 1 UNION SELECT 11, 1 UNION SELECT 12, 1 UNION SELECT 13, 1 UNION SELECT 14, 1
UNION SELECT 15, 2 UNION SELECT 16, 2 UNION SELECT 17, 2 UNION SELECT 18, 2 UNION SELECT 19, 2
UNION SELECT 20, 2 UNION SELECT 21, 2 UNION SELECT 22, 2 UNION SELECT 23, 2 UNION SELECT 24, 2
UNION SELECT 25, 2 UNION SELECT 26, 2 UNION SELECT 27, 2 UNION SELECT 28, 2 UNION SELECT 29, 2
UNION SELECT 30, 3 UNION SELECT 31, 3 UNION SELECT 32, 3 UNION SELECT 33, 3 UNION SELECT 34, 3
UNION SELECT 35, 3 UNION SELECT 36, 3 UNION SELECT 37, 3 UNION SELECT 38, 3 UNION SELECT 39, 3
UNION SELECT 40, 3 UNION SELECT 41, 3 UNION SELECT 42, 3 UNION SELECT 43, 3 UNION SELECT 44, 3
UNION SELECT 45, 4 UNION SELECT 46, 4 UNION SELECT 47, 4 UNION SELECT 48, 4 UNION SELECT 49, 4
UNION SELECT 50, 4 UNION SELECT 51, 4 UNION SELECT 52, 4 UNION SELECT 53, 4 UNION SELECT 54, 4
UNION SELECT 55, 4 UNION SELECT 56, 4 UNION SELECT 57, 4 UNION SELECT 58, 4 UNION SELECT 59, 4
)
,Time_Interval([timestamp], value, [date], [hour], minute_group)
AS
(
SELECT A.[Timestamp]
,A.value
,CONVERT(smalldatetime, CONVERT(char(10), A.[Timestamp], 101))
,DATEPART(HOUR, A.[Timestamp])
,B.minute_group
FROM myTable A
JOIN Interval_Helper B
ON (DATEPART(minute, A.[Timestamp])) = B.[minute]
AND A.[Timestamp] >= '12/01/2012'
AND A.[Timestamp] <= '01/10/2013'
AND A.ConfigID = 435
)
,Time_Interval_TimeGroup([date], [hour], [minute], MaxTimestamp)
AS
(
SELECT [date]
,[hour]
,minute_group
,MAX([Timestamp]) as MaxTimestamp
FROM Time_Interval
GROUP BY [date]
,[hour]
,minute_group
)
,Time_Interval_TimeGroup_Latest(MaxTimestamp, MaxIdx)
AS
(
SELECT MaxTimestamp
,MAX(idx) as MaxIdx
FROM myTable A
JOIN Time_Interval_TimeGroup B
ON A.[Timestamp] = B.MaxTimestamp
GROUP BY MaxTimestamp
)
SELECT A.*
FROM myTable A
JOIN Time_Interval_TimeGroup_Latest B
ON A.idx = B.MaxIdx
ORDER BY A.[timestamp]
This is another take on the clever time group function from #MntManChris below:
CREATE FUNCTION dbo.fGetTimeGroup (#DatePart tinyint, #Date datetime)
RETURNS int
AS
BEGIN
RETURN CASE #DatePart
WHEN 1 THEN DATEPART(mi, #Date)
WHEN 2 THEN DATEPART(mi, #Date)/5 + 1 -- 5 min
WHEN 3 THEN DATEPART(mi, #Date)/15 + 1 -- 15 min
WHEN 4 THEN DATEPART(mi, #Date)/30 + 1 -- 30 min
WHEN 5 THEN DATEPART(hh, #Date) -- hr
WHEN 6 THEN DATEPART(hh, #Date)/6 + 1 -- 6 hours
WHEN 7 THEN DATEPART(hh, #Date)/12 + 1 -- 12 hours
WHEN 8 THEN DATEPART(d, #Date) -- day
ELSE -1
END
END
If you want to partition in 15 minute interval use datediff in minutes and divide by 15.
And use that partition to rank each interval.
WITH myTbl AS
(
SELECT
timestamp, value,
RANK() OVER (PARTITION BY (DATEDIFF(Mi,0, Timestamp)/15) ORDER BY Timestamp desc) RK
FROM myTable
--WHERE Timestamp BETWEEN '' AND ''
)
SELECT * FROM myTble
WHERE RK <= 1
As my comment above says I've used Rob's answer but implmented a user function to eliminate the Interval_Helper table and the first join. Here is the code for the user function.
BEGIN
DECLARE #Ans integer
if #DatePart = 1 -- min
return DATEPART(mi, #Date)
if #DatePart = 2 -- 5 min
return DatePart(mi,#Date)/5 + 1
if #DatePart = 3 -- 15 min
return DatePart(mi,#Date)/15 + 1
if #DatePart = 4 -- 30min
return DatePart(mi,#Date)/30 + 1
if #DatePart = 5 -- hr
return DATEPART(hh, #Date)
if #DatePart = 6 -- 6 hours
return DATEPART(hh, #Date)/6 + 1
if #DatePart = 7 -- 12 hours
return DATEPART(hh, #Date)/12 + 1
if #DatePart = 8 -- day
return DATEPART(d, #Date)
return -1
END
This then made the Time_Interval table look like
;WITH Time_Interval([timestamp], value, [date], [day], time_group)
AS
(
SELECT A.[Timestamp]
,A.value
,CONVERT(smalldatetime, CONVERT(char(10), A.[Timestamp], 101))
,DATEPART(dd, A.[Timestamp])
,dbo.fGetTimeGroup(#tInterval, A.[Timestamp]) as 'time_group'
FROM myTable A
where
A.[Timestamp] >= '12/01/2012'
AND A.[Timestamp] <= '01/10/2013'
AND A.ConfigID= 435
)
Since there is a switch from "hours" to "days" as the #TimeInterval goes from 1hr to 6hr, or 12hr or every day. I also had to have the Time_Interval_TimeGroup table switch from grouping by [hour] to grouping by [day] and of course having this in the select list.
Since this is part of a much larger abstract DB schema where both the table in question and the db are functions of the ConfigID and thus required dynamic SQL, implmenting this switch in grouping was not an issue, I simply implmented two different dynSql sections based on the value of #TimeInterval
Thanks

a SQL query for sum of all the values of a column

i want a sql query the result like the below :
LineNumber UnitPrice Quantity
1 14 12
2 09 10
3 34 5
4 18 9
5 42 40
6 07 10
7 45 15
-----
101
pls help me....
Another way
WITH YourTable(LineNumber, UnitPrice, Quantity)
AS (SELECT 1, 14,12
UNION ALL
SELECT 2, 09, 10
UNION ALL
SELECT 3, 34, 5
UNION ALL
SELECT 4, 18, 9
UNION ALL
SELECT 5, 42, 40
UNION ALL
SELECT 6, 07, 10
UNION ALL
SELECT 7, 45, 15)
SELECT LineNumber,
UnitPrice,
SUM(Quantity) AS Quantity
FROM YourTable
GROUP BY GROUPING SETS ( ( LineNumber, UnitPrice, Quantity ), ( ) )
To get the total you will use an aggregate:
select sum(quantity) Total
from yourtable
To return the data from your table:
select LineNumber, UnitPrice, Quantity
from yourTable
To return them together you can use a UNION ALL:
select LineNumber, UnitPrice, Quantity
from yourTable
UNION ALL
select 0, 0, sum(quantity) Total
from yourtable
See SQL Fiddle with Demo
-- For all the data from the table
SELECT [LineNumber], [UnitPrice], [Quantity] FROM [SomeTable]
-- For the sum of the quantity field.
SELECT SUM([Quantity]) AS [Sum] FROM [SomeTable]

Find top sales people

I want to find Top and botton 10% sales people.How can I do this using SQL 2005 or 2008?
DECLARE #Sales TABLE
(
SalesPersonID varchar(10), TotalSales int
)
INSERT #Sales
SELECT 1, 200 UNION ALL
SELECT 2, 300 UNION ALL
SELECT 7, 300 UNION ALL
SELECT 4, 100 UNION ALL
SELECT 5, 600 UNION ALL
SELECT 5, 600 UNION ALL
SELECT 2, 200 UNION ALL
SELECT 5, 620 UNION ALL
SELECT 4, 611 UNION ALL
SELECT 3, 650 UNION ALL
SELECT 7, 611 UNION ALL
SELECT 9, 650 UNION ALL
SELECT 3, 555 UNION ALL
SELECT 9, 755 UNION ALL
SELECT 8, 650 UNION ALL
SELECT 3, 620 UNION ALL
SELECT 5, 633 UNION ALL
SELECT 6, 720
GO
Also If i add department, then how can i write same query to find top 10% and bottom 10% in each department? I please want both queries.
TOP 10 %
select top 10 percent SalesPersonID, sum(TotalSales)
from Sales
order by sum(TotalSales)
group by SalesPersonID
BOTTOM 10 %
select top 10 percent SalesPersonID, sum(TotalSales)
from Sales
order by sum(TotalSales) desc
group by SalesPersonID
--Top 10%
SELECT TOP 10 PERCENT SalesPersonID, SUM(TotalSales) FROM #Sales
GROUP BY SalesPersonID
ORDER BY SUM(TotalSales) ASC
--Bottom 10%
SELECT TOP 10 PERCENT SalesPersonID, SUM(TotalSales) FROM #Sales
GROUP BY SalesPersonID
ORDER BY SUM(TotalSales) DESC
If you added a column Department varchar(20) for example:
--By Dept
SELECT TOP 10 PERCENT Department, SUM(TotalSales) FROM #Sales
GROUP BY Department
ORDER BY SUM(TotalSales) ASC/DESC //(Whichever one you want)
cte version:
DECLARE #Sales TABLE (SalesPersonID varchar(10), TotalSales int)INSERT #Sales
SELECT 1, 200 UNION ALL
SELECT 2, 300 UNION ALL
SELECT 7, 300 UNION ALL
SELECT 4, 100 UNION ALL
SELECT 5, 600 UNION ALL
SELECT 5, 600 UNION ALL
SELECT 2, 200 UNION ALL
SELECT 5, 620 UNION ALL
SELECT 4, 611 UNION ALL
SELECT 3, 650 UNION ALL
SELECT 7, 611 UNION ALL
SELECT 9, 650 UNION ALL
SELECT 3, 555 UNION ALL
SELECT 9, 755 UNION ALL
SELECT 8, 650 UNION ALL
SELECT 3, 620 UNION ALL
SELECT 5, 633 UNION ALL
SELECT 6, 720
;with a as
(
select SalesPersonID, sum(TotalSales) as Total
from #Sales
group by SalesPersonID
)
select coalesce(a.SalesPersonID, b.SalesPersonID) as SalesPersonID, coalesce(a.Total,b.Total) as Total
from a a
full outer join a b
on a.SalesPersonID=b.SalesPersonID
where a.SalesPersonID in (select top 10 percent SalesPersonID from a order by Total desc)
or b.SalesPersonID in (select top 10 percent SalesPersonID from a order by Total)
order by a.Total desc

Resources