November 30, 2006 at 3:18 pm
This may seem a very simple question, but I am trying to find a way to list say the top 5 values (or less if fewer exist) for each person in a table witout having to resort to cursors.
Here is a simplified version of the problem:
CREATE TABLE tbl1
(
id int IDENTITY NOT NULL PRIMARY KEY,
PersonCode varchar(6) NOT NULL,
DataValue int
)
go
INSERT tbl1 VALUES ('ABC123, 18')
INSERT tbl1 VALUES ('ABC123, 7')
INSERT tbl1 VALUES ('ABC123, 14')
INSERT tbl1 VALUES ('ABC123, 92')
INSERT tbl1 VALUES ('ABC123, 12')
INSERT tbl1 VALUES ('ABC123, 15')
INSERT tbl1 VALUES ('ABC123, 3')
INSERT tbl1 VALUES ('ABC123, 6')
INSERT tbl1 VALUES ('ABC123, 68')
INSERT tbl1 VALUES ('DEF456, 5')
INSERT tbl1 VALUES ('DEF456, 6')
INSERT tbl1 VALUES ('DEF456, 78')
INSERT tbl1 VALUES ('GHI789, 63')
INSERT tbl1 VALUES ('GHI789, 12')
INSERT tbl1 VALUES ('GHI789, 148')
INSERT tbl1 VALUES ('GHI789, 15')
INSERT tbl1 VALUES ('GHI789, 11')
INSERT tbl1 VALUES ('GHI789, 44')
INSERT tbl1 VALUES ('GHI789, 32')
go
Now, the results I want are:
PersonCode DataValue
---------- ---------
ABC123 92
ABC123 68
ABC123 18
ABC123 15
ABC123 14
DEF456 78
DEF456 6
DEF456 5
GHI789 148
GHI789 63
GHI789 44
GHI789 32
GHI789 15
Two of us have been struggling over this for around 3 hrs and we have the added pressure of a very tight deadline!
Hopefully someone can help before Mr Cursor comes a-callin'
November 30, 2006 at 3:37 pm
Based on your simplified example, try this:
SELECT
* FROM tbl1 a
where
a.DataValue in (select top 5 b.DataValue from tbl1 b where b.PersonCode = a.PersonCode order by b.DataValue desc
)
order
by a.PersonCode asc, a.DataValue desc
go
November 30, 2006 at 3:38 pm
Use a temp table to assign a "rank" to records based on whatever sort order you need.
Join this to itself via a derived table, to include only items of a certain rank - in your case the top 5 ranked items within each group of PersonCode.
-- Test Data
CREATE TABLE #tbl1
(
id int IDENTITY NOT NULL PRIMARY KEY,
PersonCode varchar(6) NOT NULL,
DataValue int
)
go
INSERT #tbl1 (PersonCode, DataValue) VALUES ('ABC123', 18)
INSERT #tbl1 (PersonCode, DataValue) VALUES ('ABC123', 7)
INSERT #tbl1 (PersonCode, DataValue) VALUES ('ABC123', 14)
INSERT #tbl1 (PersonCode, DataValue) VALUES ('ABC123', 92)
INSERT #tbl1 (PersonCode, DataValue) VALUES ('ABC123', 12)
INSERT #tbl1 (PersonCode, DataValue) VALUES ('ABC123', 15)
INSERT #tbl1 (PersonCode, DataValue) VALUES ('ABC123', 3)
INSERT #tbl1 (PersonCode, DataValue) VALUES ('ABC123', 6)
INSERT #tbl1 (PersonCode, DataValue) VALUES ('ABC123', 68)
INSERT #tbl1 (PersonCode, DataValue) VALUES ('DEF456', 5)
INSERT #tbl1 (PersonCode, DataValue) VALUES ('DEF456', 6)
INSERT #tbl1 (PersonCode, DataValue) VALUES ('DEF456', 78)
INSERT #tbl1 (PersonCode, DataValue) VALUES ('GHI789', 63)
INSERT #tbl1 (PersonCode, DataValue) VALUES ('GHI789', 12)
INSERT #tbl1 (PersonCode, DataValue) VALUES ('GHI789', 148)
INSERT #tbl1 (PersonCode, DataValue) VALUES ('GHI789', 15)
INSERT #tbl1 (PersonCode, DataValue) VALUES ('GHI789', 11)
INSERT #tbl1 (PersonCode, DataValue) VALUES ('GHI789', 44)
INSERT #tbl1 (PersonCode, DataValue) VALUES ('GHI789', 32)
go
-- Create an empty temp table for ranking results
Select Identity(int, 1, 1) As RankingSequence,
Cast(id as int) As Id,
PersonCode,
Datavalue
Into #Ranked
From #tbl1
Where 0 = 1
-- Populate the table, ordered by PersonCode and the value to be ranked
Insert Into #Ranked
( Id, PersonCode, Datavalue)
Select
Id, PersonCode, DataValue
From #Tbl1
Order By PersonCode, DataValue Desc
-- get the results out, including only topmost 5 per PersonCode
Select r.PersonCode, r.DataValue
From #Ranked As r
Inner Join
(
Select PersonCode, Min(RankingSequence) + 4 As Number5
From #Ranked
Group By PersonCode
) dtTop5
On (r.PersonCode = dtTop5.PersonCode And
r.RankingSequence <= dtTop5.Number5 )
Order By r.PersonCode, r.DataValue Desc
November 30, 2006 at 3:41 pm
If you're using SQL Server 2005 you should look into the ranking functions... something like this:
SELECT t1.*
FROM tbl1 t1
JOIN (SELECT id, RANK() OVER (PARTITION BY PersonCode ORDER BY DataValue DESC) AS rank_value FROM tbl1 ) t2
ON t1.id = t2.id
WHERE t2.rank_value <= 5
ORDER BY t1.PersonCode ASC, t1.DataValue DESC
November 30, 2006 at 3:44 pm
Doh, didn't read the top... 7,2000 forum. Use one of the examples above then.
December 1, 2006 at 2:48 am
Thankyou all for replying, even Aaron who inadvertently missed the forum subject title!
Lynn...your solution was the one I first tried. However it doesn't guarantee to always bring back a max of five rows per person:
set nocount on
declare @tbl table(
tbl_id int IDENTITY NOT NULL PRIMARY KEY,
PersonCode varchar(6) NOT NULL,
DataValue int)
INSERT @tbl(PersonCode, DataValue) VALUES ('ABC123', 18)
INSERT @tbl(PersonCode, DataValue) VALUES ('ABC123', 7)
INSERT @tbl(PersonCode, DataValue) VALUES ('ABC123', 14)
INSERT @tbl(PersonCode, DataValue) VALUES ('ABC123', 92)
INSERT @tbl(PersonCode, DataValue) VALUES ('ABC123', 12)
INSERT @tbl(PersonCode, DataValue) VALUES ('ABC123', 15)
INSERT @tbl(PersonCode, DataValue) VALUES ('ABC123', 3)
INSERT @tbl(PersonCode, DataValue) VALUES ('ABC123', 6)
INSERT @tbl(PersonCode, DataValue) VALUES ('ABC123', 68)
INSERT @tbl(PersonCode, DataValue) VALUES ('DEF456', 5)
INSERT @tbl(PersonCode, DataValue) VALUES ('DEF456', 6)
INSERT @tbl(PersonCode, DataValue) VALUES ('DEF456', 78)
INSERT @tbl(PersonCode, DataValue) VALUES ('GHI789', 63)
INSERT @tbl(PersonCode, DataValue) VALUES ('GHI789', 15)
INSERT @tbl(PersonCode, DataValue) VALUES ('GHI789', 148)
INSERT @tbl(PersonCode, DataValue) VALUES ('GHI789', 32)
INSERT @tbl(PersonCode, DataValue) VALUES ('GHI789', 44)
INSERT @tbl(PersonCode, DataValue) VALUES ('GHI789', 44)
INSERT @tbl(PersonCode, DataValue) VALUES ('GHI789', 32)
SELECT a.personcode, a.datavalue FROM @tbl a
where a.DataValue in
(select top 5 b.DataValue from @tbl b where b.PersonCode = a.PersonCode order by b.DataValue desc)
order by a.PersonCode asc, a.DataValue desc
Brings back:
personcode datavalue
---------- -----------
ABC123 92.00
ABC123 68.00
ABC123 18.00
ABC123 15.00
ABC123 14.00
DEF456 78.00
DEF456 6.00
DEF456 5.00
GHI789 148.00
GHI789 63.00
GHI789 44.00
GHI789 44.00
GHI789 32.00
GHI789 32.00
PW's solution does work but seemed a little complicated, so I've simplified it and it works a treat! Thankyou.
Viewing 6 posts - 1 through 5 (of 5 total)
You must be logged in to reply to this topic. Login to reply