为每个组选择前 N 行

Posted

技术标签:

【中文标题】为每个组选择前 N 行【英文标题】:Select top N rows for each group 【发布时间】:2016-09-26 10:33:45 【问题描述】:

我有以下 MS Access 数据库架构:

我想从 Items 表中选择按 Items.score 排序的行,以便每个组最多有 Group.top_count 行。

例如我在表格中有以下数据:

组表:

物品表:

我想为第 1 组选择前 2 项,为第 2 组选择前 1 项。所以结果必须包含第 1、2 和 5 行。

有一个similar question at DBA stackexchange,但是关于 SQL Server。所以所有答案都使用 SQL Server 语法,我无法将其调整为在 MS Access 上工作。

【问题讨论】:

【参考方案1】:

如果每组有一个常数,你可以这样做:

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
              );

相反,您需要枚举值,这在 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;

这个逻辑实现了row_number()的等价物,这是解决这个问题的正确方法(如果你的数据库支持的话)。

【讨论】:

【参考方案2】:

使用 VBA 创建 SQL 命令,不妨试试这个(未经测试)。它基本上创建了一个连接每个分组的 UNION,并允许您在任何大小的表上运行它(不确定 UNION 是否有限制,以及它是否在很多之后开始陷入困境,或者也许有更好的方法可以打开一个记录集/表,只需将结果写入该记录集/表,而不是执行 UNION 操作。

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

【讨论】:

以上是关于为每个组选择前 N 行的主要内容,如果未能解决你的问题,请参考以下文章

在 SQL Server 中,如何选择前 4 行?

按组选择前 N 个值

hive - 如何为每个匹配选择前 N 个元素

选择前 N 行 [重复]

如果条件满足则选择 A 行,否则为每个组选择 B 行

从 oracle 中为每个组选择最新行