【问题标题】:MS-Access Select 1 row from GROUP BY queryMS-Access 从 GROUP BY 查询中选择 1 行
【发布时间】:2021-11-15 10:12:16
【问题描述】:

我总是很难理解 GROUP BY 功能,这个也不例外。

我有一个简单的 Join 查询

Select t1.g1, t1.g2, t2.id, t2.datetime, t3.name 
From ((table1 t1 Inner Join table2 t2 on t1.fld1=t2.fld1)
Inner Join table3 t3 on t1.fld2=t3.fld2)
Order By t2.datetime, t2.id

这会按预期返回我的数据。以下是一些示例行,说明了我尝试使用 Group By 检索的内容...

t1.g1 t2.g2 t2.id t2.datetime t3.name
726 4506 32 9/12/2021 nameA
726 4506 33 9/12/2021 nameB
726 4506 30 9/13/2021 nameC

我想获取 t1.g1, t1.g2 的每组中的第一行。

所以,我尝试以下方法:

Select t1.g1, t1.g2, FIRST(t2.id), FIRST(t2.datetime), FIRST(t3.name) 
From ((table1 t1 Inner Join table2 t2 on t1.fld1=t2.fld1)
Inner Join table3 t3 on t1.fld2=t3.fld2)
Group By t1.g1, t1.g2
Order By FIRST(t2.datetime), FIRST(t2.id)

对于上面的示例组,这将返回以下记录...

t1.g1 t2.g2 t2.id t2.datetime t3.name
726 4506 30 9/13/2021 nameC

因此,Order By 在分组完成之后运行,而不是之前。或者看起来是这样。可能是 SQL 关键字顺序的原因(Select、From、Where、Group By、Order By)。好的,如果我的假设是正确的,那是有道理的。我认为它发现 t2.id=30 领先于其他 726/4506 记录,因为 t2.id 是 table2 上的主键。

所以,现在我尝试一个嵌套查询,其中我上面的第一个查询以正确的顺序和外部查询组返回数据并获取第一条记录。

Select t1.g1, t1.g2, FIRST(t2.id), FIRST(t2.datetime), FIRST(t3.name) 
FROM (
Select t1.g1, t1.g2, t2.id, t2.datetime, t3.name 
From ((table1 t1 Inner Join table2 t2 on t1.fld1=t2.fld1)
Inner Join table3 t3 on t1.fld2=t3.fld2)
Order By t2.datetime, t2.id
)
Group By t1.g1, t1.g2
Order By FIRST(t2.datetime), FIRST(t2.id)

同样的结果!我不知道这是怎么发生的。因此,如果有人能阐明在这种情况下 Access SQL 的幕后运作顺序,我很想知道。在我的第二个查询(嵌套选择)中,似乎我正在对目标数据进行排序,以便在对 FIRST() 聚合函数进行分组之后,应该选择在内部结果集中找到的第一行。但这并没有发生。

当然,如果有人能告诉我如何返回我所在的行...

t1.g1 t2.g2 t2.id t2.datetime t3.name
726 4506 32 9/12/2021 nameA

这就是我真正需要的。

【问题讨论】:

    标签: sql ms-access group-by


    【解决方案1】:

    这是一个可以很好扩展的解决方案(6s on 250k recs in t2)并且可以满足我的要求。

    我无法获得 Gordon 在 Access 中工作的答案。似乎它应该有。而且我怀疑它在 t2 中使用 250k recs 的性能有多好。如果我能弄清楚如何让 Access 接受它,我很想测试像 Gordon 的解决方案。

    请参阅问题描述以获取有关我所追求的确切记录的示例。我只需要结果集中的 t2.id 。最初没有说明这一点,但我看不出这会如何改变问题陈述或解决方案。我可能错了。我仍然需要 t3.name,但以后可以使用 t2.id 检索它。

    但是当所有记录都按 t2.dateandtime, t2.id 排序时,我仍然需要选择首先出现的记录 GROUP'd BY t1.g1, t1.g2强>。或者换一种说法,在所有具有相同 t1.g1+t1.g2 的记录中,当组按“t2.dateandtime, t2.id”排序时,我需要的正是第一条记录。

    也许我正在考虑解决我的问题的方法都错了,并且有更好的方法可以用 SQL 解决这个问题;如果是这样,我很想听。

    我似乎了解到 GROUP BY 确实基于这个 SQL 子句将记录分组在一起,但是这种分组在这一点上失去了任何单个记录的概念;例如您只能使用聚合函数(MIN、MAX、SUM 等)提取其他字段,但是 - 重要的是 - FIRST 没有获得您可以预测的记录值,因为 ORDER BY 子句尚未执行.

    说了这么多,这是我的解决方案。

    1. 我删除了对 t3 上的 Join 的引用,就像 t2.id 一样,我可以在事后使用 t2.id 从 t3 检索我需要的所有其他信息。
    2. 不需要选择't1.g1, t1.g2',那是多余的。我最初认为任何 Group By 字段也必须在 Select 子句中指定。
    3. 我将 t2.dateandtime 和 t2.id 合并到一个文本字段中,并使用 MIN 来选择我在 GROUP'd BY 之后的数据/记录。不需要对我的结果集进行排序,因为 MIN 值为 t2.dateandtime 的记录,则选择了 t2.id!从而满足我的条件并选择正确的记录。
    4. 由于我只需要返回 t2.id 以供进一步处理,因此我从 #3 中内置的 String 中提取 t2.id 并转换回 Long 数据类型。

    这是简短的查询:

    Select 
     MIN(Format(t2.dateandtime, "yyyymmddhhmmss") & '_' & Format(t2.id, '000000')) as dt_id,  
     CLNG(MID(dt_id, INSTR(dt_id, '_') + 1)) as id
    From 
     (table1 t1 Inner Join table2 t2 on t1.fld1=t2.fld1)
    Group By
     t1.g1, t1.g2
    

    【讨论】:

    • 有趣的更新。根据 Microsoft 的说法,FIRST/LAST 聚合函数应该在记录按提供的任何 ORDER BY 子句排序后运行。这与我的发现不一致。 support.microsoft.com/en-us/office/… "这些函数分别返回查询返回的结果集中的第一条或最后一条记录中指定字段的值。如果查询不包含 ORDER BY 子句,则这些函数返回的值将是任意,因为记录通常不返回任何部分..."
    【解决方案2】:

    我只想抓取每组 t1.g1、t1.g2 中的第一行。

    你不想聚合。您想要过滤数据。在这种情况下,相关的子查询可以满足您的需求:

    Select t1.g1, t1.g2, t2.id, t2.datetime, t3.name 
    From (table1 t1 Inner Join
          table2 t2
          on t1.fld1 = t2.fld1
         ) Inner Join
         table3 t3
         on t1.fld2 = t3.fld2
    where t2.id = (select top 1 tt2.id
                   from (table1 tt1 Inner Join
                         table2 tt2
                         on tt1.fld1 = tt2.fld1
                        ) Inner Join
                        table3 tt3
                        on tt1.fld2 = tt3.fld2
                   where tt1.g1 = t1.g1 and tt1.g2 = t1.g2
                   order by tt2.datetime, tt2.id
                  );
    

    【讨论】:

    • 太棒了。我花了 45 分钟来写那个问题描述,你花了大约 60 分钟来回答!在我原来的问题中,我按 t2.datetime、t2.id 订购。您的回答似乎只解决了获取 MIN 日期时间的问题。我有具有相同日期时间的记录,因此在这种情况下我需要获取具有 MIN id 值的记录。现在确定我明白在这种情况下我会怎么做;您是否愿意更新您的解决方案以包含这种情况?一旦我得到这个工作,我会测试并找出答案,但是当查询一个包含 10 万多条记录的表时,这个解决方案将如何扩展?
    • @JJJones_3860 。 . .我修改了答案以使用id 而不是datetime
    • 好的 - 谢谢,我明白为什么这应该有效。照原样,我收到错误“此子查询最多可以返回一条记录”。这似乎非常奇怪,因为我们显然使用的是 TOP 1。我玩了很多次,它似乎与子查询中的“order by”子句有问题。如果我删除这个子句,查询就可以工作,但当然,它选择了错误的记录。重新添加 Order By 子句,我得到了错误。我已经检查并仔细检查了我的语法。它和你一样,对我来说肯定很好看。有什么想法吗?
    • @JJJones_3860 。 . .尝试不使用 1 周围的括号。我不认为 MS Access 允许这些。
    • 我已经删除了“1”周围的括号。嗯。奇怪的错误。
    猜你喜欢
    • 1970-01-01
    • 2021-10-27
    • 2015-05-02
    • 1970-01-01
    • 2020-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多