问题
假设我有这张桌子tab
(小提琴可用)。
| g | a | b | v |
---------------------
| 1 | 3 | 5 | foo |
| 1 | 4 | 7 | bar |
| 1 | 2 | 9 | baz |
| 2 | 1 | 1 | dog |
| 2 | 5 | 2 | cat |
| 2 | 5 | 3 | horse |
| 2 | 3 | 8 | pig |
我按 对行进行分组g
,并且对于每个组,我想要一个来自 column 的值v
。但是,我不想要任何值,但我想要来自带有 maximal 的行的值a
,以及来自所有这些的带有 maximal 的行的值b
。换句话说,我的结果应该是
| 1 | bar |
| 2 | horse |
当前解决方案
我知道一个查询来实现这一点:
SELECT grps.g,
(SELECT v FROM tab
WHERE g = grps.g
ORDER BY a DESC, b DESC
LIMIT 1) AS r
FROM (SELECT DISTINCT g FROM tab) grps
问题
但我认为这个查询相当难看。主要是因为它使用了一个依赖子查询,这感觉就像一个真正的性能杀手。所以我想知道这个问题是否有更简单的解决方案。
预期答案
我对这个问题最有可能的答案是 MySQL(或 MariaDB)的某种附加组件或补丁,它确实为此提供了一个功能。但我也欢迎其他有用的灵感。任何没有依赖子查询的工作都可以作为答案。
如果您的解决方案仅适用于单个排序列,即无法区分cat
and horse
,请随意提出该答案,因为我希望它对大多数用例仍然有用。例如,100*a+b
一种可能的方法是按两列对上述数据进行排序,同时仍然只使用一个表达式。
我有一些非常老套的解决方案,可能会在一段时间后添加它们,但我会先看看是否有一些不错的新解决方案会先出现。
基准测试结果
由于仅通过查看它们很难比较各种答案,因此我对它们进行了一些基准测试。这是在我自己的桌面上运行的,使用 MySQL 5.1。这些数字不会与任何其他系统进行比较,只能相互比较。如果性能对您的应用程序至关重要,您可能应该使用您的真实数据进行自己的测试。当有新的答案出现时,我可能会将它们添加到我的脚本中,然后重新运行所有测试。
- 100,000 个项目,1,000 个组可供选择,InnoDb:
- MvG 0.166s(来自问题)
- RichardTheKiwi 为0.520 秒
- xdazz 2.199s
- Dems 19.24s (顺序子查询)
- 48.72s for acatt
- 100,000 个项目,50,000 个组可供选择,InnoDb:
- xdazz 为0.356 秒
- 0.640s for RichardTheKiwi
- MvG 0.764s(来自问题)
- 51.50s for acatt
- 对Dems来说太长(顺序子查询)
- 100,000 个项目,100 个组可供选择,InnoDb:
- MvG 0.163s(来自问题)
- RichardTheKiwi 为0.523 秒
- Dems 2.072s (顺序子查询)
- xdazz 为17.78 秒
- 49.85s for acatt
因此,到目前为止,我自己的解决方案似乎并不是那么糟糕,即使使用依赖子查询也是如此。令人惊讶的是,acatt 的解决方案也使用了依赖子查询,因此我认为它的性能要差得多。可能是 MySQL 优化器无法处理的问题。RichardTheKiwi 提出的解决方案似乎也具有良好的整体性能。其他两种解决方案在很大程度上取决于数据的结构。对于许多小团体,xdazz 的方法优于所有其他方法,而 Dems 的解决方案在少数大型团体中表现最好(尽管仍然不是特别好)。