If this is your first visit, be sure to check out the FAQ by clicking the link above. You may have to register before you can post: click the register link above to proceed. To start viewing messages, select the forum that you want to visit from the selection below.

 
Go Back  dBforums > Database Server Software > Microsoft SQL Server > How Can I obtain the Top 3 of a dataset

Reply
 
LinkBack Thread Tools Display Modes
  #1 (permalink)  
Old 02-09-05, 11:19
dolfandave dolfandave is offline
Registered User
 
Join Date: Feb 2004
Posts: 181
How Can I obtain the Top 3 of a dataset

If I have say 20 records of sales employees, for example, how can I get the top 3 locations for $$$sales for EACH employee? Each employee can have multiple locations where they have sold(let's say up to 50). I only want the names of the top 3 locations. The closest I can get is filtering the dataset by a HAVING clause > a dollar amount but this still gives me between 3 - 12 records for each plus I have to literally enter each salesperson's number as it stands now. Is this a loop or a cursor? Thanks.

ddave
Reply With Quote
  #2 (permalink)  
Old 02-09-05, 11:30
HardCode HardCode is offline
Registered User
 
Join Date: Oct 2004
Location: New Jersey
Posts: 60
Once you have your SQL set up to return the sales dollars for all of the locations, just add TOP 3 before the select:
Code:
 SELECT TOP 3 Sum(Dollars) FROM MyTable;
Note that you can also SELECT TOP n PERCENT.
__________________
Retired Moderator at Xtreme VB Talk, for all of your Visual Basic needs.
Reply With Quote
  #3 (permalink)  
Old 02-09-05, 12:04
blindman blindman is offline
World Class Flame Warrior
 
Join Date: Jun 2003
Location: Ohio
Posts: 10,870
General solution to your problem:

The TOP N clause in TSQL is useful, but is limited by the fact that it only applies to the entire recordset, and that it will not accept a variable as a parameter.

This method will return any requested number of records for each group of one or more column values in a dataset. It uses a type of join called a Theta join, where the values in two datasets are compared, but do not necessarily have to be equal:

Code:
 
declare @N int
set     @N = 5  --The number of records to return for each grouping.
select  YourTable.YourColumns
from    YourTable
        inner join YourTable ThetaTable
        on YourTable.GroupColumns = ThetaTable.GroupColumns
        and YourTable.SortColumn <= ThetaTable.SortColumn
group by YourTable.YourColumns
having count(*) <= @N
__________________
If it's not practically useful, then it's practically useless.

blindman
www.chess.com: "sqlblindman"
Reply With Quote
  #4 (permalink)  
Old 02-09-05, 12:11
dolfandave dolfandave is offline
Registered User
 
Join Date: Feb 2004
Posts: 181
The TOP function does not work as blindman indicates as it gives me only the top 3 of 120+ records, not the top 3 for EACH person. I will try the Theta join. Thanks to both for replying.

ddave
Reply With Quote
  #5 (permalink)  
Old 02-09-05, 14:10
r937 r937 is offline
Registered User
 
Join Date: Apr 2002
Location: Toronto, Canada
Posts: 16,930
blindman, your queries are starting to look good

but the join should be outdented

use foo and bar if more than one column is intended (YourColumns alone does not show comma syntax)

and shorter aliases make it easier to read
Code:
select  T.foo, T.bar
from    YourTable as T
  inner join YourTable as Th
        on T.GroupColumns = Th.GroupColumns
        and T.SortColumn <= Th.SortColumn
group by T.foo, T.bar
having count(*) <= n
__________________
r937.com | rudy.ca
please visit Simply SQL and buy my book
Reply With Quote
  #6 (permalink)  
Old 02-09-05, 15:41
Brett Kaiser Brett Kaiser is offline
SQLTeam Scrub
 
Join Date: Nov 2002
Location: Jersey
Posts: 9,834
Quote:
Originally Posted by r937
blindman, your queries are starting to look good
Looks can be deceiving...it doesn't work

Code:
USE Northwind
GO

SET NOCOUNT ON
CREATE TABLE myTable99(SiteId int, EmpId int, Sales money)
GO

INSERT INTO myTable99(SiteId, EmpId, Sales)
SELECT 1, 1, 10.00 UNION ALL
SELECT 2, 1, 15.00 UNION ALL
SELECT 3, 1, 20.00 UNION ALL
SELECT 4, 1, 50.00 UNION ALL
SELECT 5, 1, 10.00 UNION ALL
SELECT 6, 1, 5.00 UNION ALL
SELECT 1, 2, 100.00 UNION ALL
SELECT 2, 2, 1500.00 UNION ALL
SELECT 3, 2, 2000.00 UNION ALL
SELECT 4, 2, 5000.00 UNION ALL
SELECT 5, 2, 1000.00 UNION ALL
SELECT 6, 2, 500.00 UNION ALL
SELECT 1, 3, 1.00 UNION ALL
SELECT 2, 3, 1.50 UNION ALL
SELECT 3, 3, 2.00 UNION ALL
SELECT 4, 3, 5.00 UNION ALL
SELECT 5, 3, 1.00 UNION ALL
SELECT 6, 3, .50
GO

 DECLARE @N int
     SET @N = 3  --The number of records to return for each grouping.

  SELECT a.SiteId, a.EmpId
    FROM myTable99 a 
    JOIN myTable99 b
      ON a.SiteId  = b.SiteId
     AND a.EmpId   = b.EmpId
     AND a.Sales < = b.Sales
GROUP BY a.SiteId, a.EmpId
  HAVING COUNT(*) <= @N
GO

SET NOCOUNT OFF
DROP TABLE myTable99
GO
I know I've seen this work somehow though
__________________
Brett
8-)

It's a Great Day for America everybody!

My Blog dbforums Yak CorralRadio 'Rita
dbForums Member List SQLTeam Member List
It's 5:00 Somewhere Pearls
The physical order of data in a database has no meaning.
Reply With Quote
  #7 (permalink)  
Old 02-09-05, 17:49
Pat Phelan Pat Phelan is offline
Resident Curmudgeon
 
Join Date: Feb 2004
Location: In front of the computer
Posts: 11,160
At least to me, it is a lot more intuitively obvious to do this via a sub-query, something like:
Code:
 DECLARE @N int
     SET @N = 3  --The number of records to return for each grouping.

SELECT a.SiteId, a.EmpId, a.Sales
   FROM myTable99 a 
   WHERE (SELECT Count(*)
      FROM myTable99 b
      WHERE  b.EmpId   = a.EmpId
         AND a.Sales  <= b.Sales) <= @N
   ORDER BY a.EmpID, a.Sales DESC, a.SiteID
The problem with any set-based solution to this kind of problem is that it does not deal well with "ties" in the data... They make your results a bit "funky", but I don't know any reliable way to resolve that using set based logic.

-PatP
Reply With Quote
  #8 (permalink)  
Old 02-09-05, 19:05
r937 r937 is offline
Registered User
 
Join Date: Apr 2002
Location: Toronto, Canada
Posts: 16,930
Quote:
Originally Posted by Brett Kaiser
I know I've seen this work somehow though
yeah, it's tricky

here's what you were searching for --
Code:
  select a.EmpId
       , a.SiteId
       , a.sales
    from myTable99 a
  inner
    join myTable99 b
      on a.EmpId  = b.EmpId
     and a.Sales <= b.Sales
  group 
      by a.EmpId
       , a.SiteId
       , a.sales
  having count(*) <= 3
  order 
      by a.EmpId
       , a.sales desc
which produces the following (correct) results:
Code:
1	4	50.0000
1	3	20.0000
1	2	15.0000

2	4	5000.0000
2	3	2000.0000
2	2	1500.0000

3	4	5.0000
3	3	2.0000
3	2	1.5000
pat, i like the subquery method too, i guess i just got used to the join solution after so many times showing pre-4.1 mysql people how to do it

__________________
r937.com | rudy.ca
please visit Simply SQL and buy my book
Reply With Quote
  #9 (permalink)  
Old 02-09-05, 21:44
blindman blindman is offline
World Class Flame Warrior
 
Join Date: Jun 2003
Location: Ohio
Posts: 10,870
I indents 'em as I wants 'em, thank you!
__________________
If it's not practically useful, then it's practically useless.

blindman
www.chess.com: "sqlblindman"
Reply With Quote
  #10 (permalink)  
Old 02-10-05, 09:37
Brett Kaiser Brett Kaiser is offline
SQLTeam Scrub
 
Join Date: Nov 2002
Location: Jersey
Posts: 9,834
Hey thanks guys...

And yes Pat, I agree it may be more intuitive...but have a look at the plans...it looks like Rudy's Join is more effecient...even with the group by.

I would not have guessed this. Anyone care if I blog this? With appropriate references of course.

It's just one of things I know can be done...and I forget how to contruct it.

Anyway, here's the plans
Attached Images
File Type: bmp Plans.bmp (486.1 KB, 665 views)
__________________
Brett
8-)

It's a Great Day for America everybody!

My Blog dbforums Yak CorralRadio 'Rita
dbForums Member List SQLTeam Member List
It's 5:00 Somewhere Pearls
The physical order of data in a database has no meaning.
Reply With Quote
  #11 (permalink)  
Old 02-10-05, 09:43
r937 r937 is offline
Registered User
 
Join Date: Apr 2002
Location: Toronto, Canada
Posts: 16,930
blog away

__________________
r937.com | rudy.ca
please visit Simply SQL and buy my book
Reply With Quote
  #12 (permalink)  
Old 02-10-05, 10:04
Brett Kaiser Brett Kaiser is offline
SQLTeam Scrub
 
Join Date: Nov 2002
Location: Jersey
Posts: 9,834
OK, You've all been Blogged
__________________
Brett
8-)

It's a Great Day for America everybody!

My Blog dbforums Yak CorralRadio 'Rita
dbForums Member List SQLTeam Member List
It's 5:00 Somewhere Pearls
The physical order of data in a database has no meaning.
Reply With Quote
  #13 (permalink)  
Old 02-10-05, 10:24
r937 r937 is offline
Registered User
 
Join Date: Apr 2002
Location: Toronto, Canada
Posts: 16,930
Quote:
Originally Posted by Brett Kaiser
OK, You've all been Blogged
ta very much

and usually i just ignore your frequent typos, but in this case, i must insist that you correct the spelling of my surname

thanks in advance
__________________
r937.com | rudy.ca
please visit Simply SQL and buy my book
Reply With Quote
  #14 (permalink)  
Old 02-10-05, 10:55
Brett Kaiser Brett Kaiser is offline
SQLTeam Scrub
 
Join Date: Nov 2002
Location: Jersey
Posts: 9,834
mia culpa...done
__________________
Brett
8-)

It's a Great Day for America everybody!

My Blog dbforums Yak CorralRadio 'Rita
dbForums Member List SQLTeam Member List
It's 5:00 Somewhere Pearls
The physical order of data in a database has no meaning.
Reply With Quote
  #15 (permalink)  
Old 02-10-05, 11:03
r937 r937 is offline
Registered User
 
Join Date: Apr 2002
Location: Toronto, Canada
Posts: 16,930
Quote:
Originally Posted by Brett Kaiser
mia culpa...done
thanks

and now, would you mind changing the link so that it points to the correct person

__________________
r937.com | rudy.ca
please visit Simply SQL and buy my book
Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On