为什么这个 ORDER BY in sub-query workaround 不能始终如一地工作?
Why does this ORDER BY in sub-query workaround not work consistently?
为了获取某个标识符组合的最近记录,我使用以下查询:
SELECT t1.*
FROM (
SELECT id, b_id, c_id
FROM a
ORDER BY epoch DESC
LIMIT 18446744073709551615
) AS t1
GROUP BY t1.b_id, t1.c_id
如果 b_id
+ c_id
的组合有多个记录,那么它总是 select 具有最高值 epoch
的那个(并且作为这样,最晚的时间)。
添加 LIMIT
作为解决方法 to force MariaDB to actually order the results. I successfully use this construction a lot in my application, and so have others。
但是,现在我在我的应用程序中遇到了一个完全相同的查询,我“不小心”在子查询中使用了比绝对必要的列更多的列:
SELECT t1.*
FROM (
SELECT id, b_id, c_id, and, some, other, columns, ...
FROM a
ORDER BY epoch DESC
LIMIT 18446744073709551615
) AS t1
GROUP BY t1.b_id, t1.c_id
我已经测试了这两个查询。而完全相同的查询,但只更改那些额外的列,会使结果变得不正确。事实上,列数决定了结果。如果我有 <= 28 列,则结果没问题。如果我有 29 列,那么它给出倒数第三的记录(这也是错误的),如果我有 30-36 列,它总是给出倒数第二的记录(36 是 table a
).在我的测试中,删除或添加哪个特定列似乎并不重要。
我很难找出添加更多列后行为发生变化的确切原因。另外,也许是偶然,它昨天仍然给出了正确的结果。但是今天结果突然变了,可能是在 table a
中添加了新记录(带有不相关的标识符)之后。我试过使用 EXPLAIN
:
# The first query, with columns: id, b_id, c_id
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 280 Using where; Using temporary; Using filesort
2 DERIVED a ALL NULL NULL NULL NULL 280 Using filesort
# The second query, with columns: id, b_id, c_id, and, some, other, columns, ...
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 276 Using where; Using temporary; Using filesort
2 DERIVED a ALL NULL NULL NULL NULL 276 Using filesort
但这并没有真正帮助我,除此之外我可以看到 key_len
是不同的。在第二个查询中错误接收的第二个最新记录是 id = 276
,使用第一个查询正确检索的实际最新记录是 id = 278
。现在总共有 307 行,而昨天可能只有 ~300 行。我不确定如何解释这些结果以了解出了什么问题。有人知道吗?如果不是,我还能做些什么来找出导致这些奇怪结果的原因?
为什么不使用 window 函数而不是这个肮脏的解决方法,它依赖于 MySQL/MariaDB non-standard 关于 group by
的行为?
select *
from (
select a.*, row_number() over(partition by b_id, c_id order by epoch desc) rn
from a
) a
where rn = 1
这适用于 MySQL 8.0 和 Maria DB 10.2 或更高版本。在早期版本中,一种替代方法是相关子查询:
select *
from a
where epoch = (select max(a1.epoch) from a a1 where a1.b_id = a.b_id and a1.c_id = a.c_id)
这是一个格式错误的查询,应该会产生语法错误:
SELECT t1.*
FROM (SELECT id, b_id, c_id
FROM a
ORDER BY epoch DESC
LIMIT 18446744073709551615
) t1
GROUP BY t1.b_id, t1.c_id;
为什么?您正在选择没有聚合函数的 3 列。但是 group by
只有两列。令人高兴的是,这现在是 MySQL 中的语法错误,使用默认设置。最后! (MySQL 在 8.0 版之前接受了这种 non-standard 语法。)
您可以使用相关子查询做您想做的事情:
select a.*
from a
where a.epoch = (select max(a2.epoch)
from a a2
where a2.b_id = a.b_id and a2.c_id = a.c_id
);
使用 a(b_id, c_id, epoch)
上的索引,这可能也比聚合更快——即使它在某些情况下碰巧起作用。
为了获取某个标识符组合的最近记录,我使用以下查询:
SELECT t1.*
FROM (
SELECT id, b_id, c_id
FROM a
ORDER BY epoch DESC
LIMIT 18446744073709551615
) AS t1
GROUP BY t1.b_id, t1.c_id
如果 b_id
+ c_id
的组合有多个记录,那么它总是 select 具有最高值 epoch
的那个(并且作为这样,最晚的时间)。
添加 LIMIT
作为解决方法 to force MariaDB to actually order the results. I successfully use this construction a lot in my application, and so have others。
但是,现在我在我的应用程序中遇到了一个完全相同的查询,我“不小心”在子查询中使用了比绝对必要的列更多的列:
SELECT t1.*
FROM (
SELECT id, b_id, c_id, and, some, other, columns, ...
FROM a
ORDER BY epoch DESC
LIMIT 18446744073709551615
) AS t1
GROUP BY t1.b_id, t1.c_id
我已经测试了这两个查询。而完全相同的查询,但只更改那些额外的列,会使结果变得不正确。事实上,列数决定了结果。如果我有 <= 28 列,则结果没问题。如果我有 29 列,那么它给出倒数第三的记录(这也是错误的),如果我有 30-36 列,它总是给出倒数第二的记录(36 是 table a
).在我的测试中,删除或添加哪个特定列似乎并不重要。
我很难找出添加更多列后行为发生变化的确切原因。另外,也许是偶然,它昨天仍然给出了正确的结果。但是今天结果突然变了,可能是在 table a
中添加了新记录(带有不相关的标识符)之后。我试过使用 EXPLAIN
:
# The first query, with columns: id, b_id, c_id
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 280 Using where; Using temporary; Using filesort
2 DERIVED a ALL NULL NULL NULL NULL 280 Using filesort
# The second query, with columns: id, b_id, c_id, and, some, other, columns, ...
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 276 Using where; Using temporary; Using filesort
2 DERIVED a ALL NULL NULL NULL NULL 276 Using filesort
但这并没有真正帮助我,除此之外我可以看到 key_len
是不同的。在第二个查询中错误接收的第二个最新记录是 id = 276
,使用第一个查询正确检索的实际最新记录是 id = 278
。现在总共有 307 行,而昨天可能只有 ~300 行。我不确定如何解释这些结果以了解出了什么问题。有人知道吗?如果不是,我还能做些什么来找出导致这些奇怪结果的原因?
为什么不使用 window 函数而不是这个肮脏的解决方法,它依赖于 MySQL/MariaDB non-standard 关于 group by
的行为?
select *
from (
select a.*, row_number() over(partition by b_id, c_id order by epoch desc) rn
from a
) a
where rn = 1
这适用于 MySQL 8.0 和 Maria DB 10.2 或更高版本。在早期版本中,一种替代方法是相关子查询:
select *
from a
where epoch = (select max(a1.epoch) from a a1 where a1.b_id = a.b_id and a1.c_id = a.c_id)
这是一个格式错误的查询,应该会产生语法错误:
SELECT t1.*
FROM (SELECT id, b_id, c_id
FROM a
ORDER BY epoch DESC
LIMIT 18446744073709551615
) t1
GROUP BY t1.b_id, t1.c_id;
为什么?您正在选择没有聚合函数的 3 列。但是 group by
只有两列。令人高兴的是,这现在是 MySQL 中的语法错误,使用默认设置。最后! (MySQL 在 8.0 版之前接受了这种 non-standard 语法。)
您可以使用相关子查询做您想做的事情:
select a.*
from a
where a.epoch = (select max(a2.epoch)
from a a2
where a2.b_id = a.b_id and a2.c_id = a.c_id
);
使用 a(b_id, c_id, epoch)
上的索引,这可能也比聚合更快——即使它在某些情况下碰巧起作用。