简体   繁体   中英

Select top N rows for each group

I have the following MS Access DB schema:

数据库架构

I want to select rows from Items table ordered by Items.score so that there are at most Group.top_count rows for each group.

For example I have the following data in the tables:

Group table:

组表

Items table:

物品表

I want to select top 2 items for group #1 and top 1 item for group #2. So the result must contain rows 1, 2 and 5.

There was a similar question at DBA stackexchange , but about SQL Server. So all answers used SQL Server syntax and I couldn't adapt it to work on MS Access.

If there were a constant number per group, you could do:

select i.*
from items as i inner join
     groups as g
     on i.group_id = g.id
where i.id in (select top 2 i2.id
               from items i2
               where i2.group_id = i.group_id
               order by i2.score desc
              );

Instead, you will need to enumerate the values and this is expensive in MS Access:

select i.*
from (select i.*,
             (select count(*)
              from items i2
              where i2.group_id = i.group_id and
                    (i2.score < i.score or
                     i2.score = i.score and i2.id <= i2.id
                    )
             ) as seqnum
      from items as i
     ) as i inner join
     groups as g
     on i.group_id = g.id
where i.seqnum <= g.top_count;

This logic implements the equivalent of row_number() , which is the right way to solve this problem (if your database supports it).

Using VBA to create the SQL command, perhaps try this (untested). It basically creates a UNION that joins each grouping, and allows you to run it on any size table (not sure if there's a limit to the UNION and if it starts to bog down after many, or perhaps there's a better method where you can open a recordset/table and just write the results into that recordset/table instead of doing the UNION thing.

SET DBS = CURRENTDB    
strSQL = ""
intMax = dmax("ID", "group")

FOR i = 1 TO intMax
    strSQL = strSQL & "SELECT TOP " & DLOOKUP("top_count","group","ID = " & I) & " ID " & _
        "FROM items WHERE group_id = " & i & " ORDER BY score "

    if i < intMax
        strSQL = strSQL & " UNION "
    endif
next i

dbs.execute strSQL

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM