假设我有这张表tab
(fiddle可用)。
| 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
对行进行分组,对于每个组,我想要列v
中的一个值。但是,我不希望任何值,但我希望来自行最大a
的值,以及所有这些值中最大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
和horse
,请随意提出答案以及我希望它对大多数用例。例如,100*a+b
可能是两列都对上述数据进行排序的可能方式,同时仍然只使用一个表达式。
我有一些非常讨厌的解决方案,可能会在一段时间后添加它们,但我会首先看一下,看看是否有一些不错的新解决方案首先注入。
由于很难通过查看它们来比较各种答案,我已经对它们进行了一些基准测试。这是使用MySQL 5.1在我自己的桌面上运行的。这些数字不会与任何其他系统相比,只能相互比较。如果性能对您的应用程序至关重要,您可能应该使用现实数据进行自己的测试。当新答案出现时,我可能会将它们添加到我的脚本中,然后重新运行所有测试。
所以看起来到目前为止我自己的解决方案并不是那么糟糕,即使是依赖子查询。令人惊讶的是,acatt的解决方案也使用了一个依赖子查询,因此我考虑过这个解决方案,它的表现要差得多。可能是MySQL优化器无法应对的。 RichardTheKiwi提出的解决方案似乎也具有良好的整体表现。另外两种解决方案在很大程度上取决于数据的结构。由于许多小组小组,xdazz的方法优于其他所有小组,而Dems的解决方案对于少数大型小组表现最佳(尽管仍然不是特别好)。
答案 0 :(得分:5)
这种方式不使用子查询。
SELECT t1.g, t1.v
FROM tab t1
LEFT JOIN tab t2 ON t1.g = t2.g AND (t1.a < t2.a OR (t1.a = t2.a AND t1.b < t2.b))
WHERE t2.g IS NULL
的说明:强> 的
LEFT JOIN的工作原理是,当t1.a处于最大值时,没有s2.a具有更大的值,s2行值将为NULL。
答案 1 :(得分:5)
SELECT g, a, b, v
FROM (
SELECT *,
@rn := IF(g = @g, @rn + 1, 1) rn,
@g := g
FROM (select @g := null, @rn := 0) x,
tab
ORDER BY g, a desc, b desc, v
) X
WHERE rn = 1;
单通。所有其他解决方案对我来说都是O(n ^ 2)。
答案 2 :(得分:1)
许多RDBMS都有特别适合这个问题的结构。 MySQL 不是 其中之一。
这将引导您采用三种基本方法。
使用EXISTS和EXISTS子句中的相关子查询,检查每条记录以查看它是否是您想要的记录。 (@ acatt的答案,但我知道MySQL并不总能很好地优化这一点。确保在(g,a,b)
上有一个复合索引,然后再假设MySQL不能很好地做到这一点。)
做半笛卡尔产品以完全填写相同的支票。任何未加入的记录都是目标记录。如果每个组('g')很大,这会很快降低性能(如果g
的每个唯一值有10条记录,这将产生约50条记录并丢弃49.对于组大小100,它产生约5000条记录并丢弃4999),但 非常适合小组规模。 (@ xdazz的回答。)
或使用多个子查询来确定MAX(a)然后再确定MAX(b)......
多个连续子查询...
SELECT
yourTable.*
FROM
(SELECT g, MAX(a) AS a FROM yourTable GROUP BY g ) AS searchA
INNER JOIN
(SELECT g, a, MAX(b) AS b FROM yourTable GROUP BY g, a) AS searchB
ON searchA.g = searchB.g
AND searchA.a = searchB.a
INNER JOIN
yourTable
ON yourTable.g = searchB.g
AND yourTable.a = searchB.a
AND yourTable.b = searchB.b
根据MySQL如何优化第二个子查询,这可能会或可能不会比其他选项更高效。但是,它是给定任务的最长的(可能是最不可维护的)代码。
假设所有三个搜索字段(g, a, b)
都有一个综合索引,我认为它最适合g
的大型组大小。但那应该进行测试。
对于g
的小组,我会选择@ xdazz的答案。
修改强>
还有一种蛮力方法。
SELECT g, MAX(id)
找到ID。 v
值。 这不太可能是最好的方法。如果是这样,它实际上是MySQL优化者处理这类问题的能力的基础。
也就是说,每个发动机都有它的弱点。所以,就个人而言,我会尝试一切,直到我认为我理解RDBMS的行为方式并且可以做出我的选择:)
修改强>
使用ROW_NUMBER()
的示例。 (Oracle,SQL Server,PostGreSQL等)
SELECT
*
FROM
(
SELECT
ROW_NUMBER() OVER (PARTITION BY g ORDER BY a DESC, b DESC) AS sequence_id,
*
FROM
yourTable
)
AS data
WHERE
sequence_id = 1
答案 3 :(得分:0)
这可以使用相关查询来解决:
SELECT g, v
FROM tab t
WHERE NOT EXISTS (
SELECT 1
FROM tab
WHERE g = t.g
AND a > t.a
OR (a = t.a AND b > t.b)
)