Mysql 列表中按两列排序的前一项
假设我们有一个包含DAY和NUMERO列的表 可以有许多行具有相同的DAY值,但NUMERO始终是唯一的 在一个按天排序的列表中,最有效的方法是什么,在一个现有的两天之前获取项目 我精确地说,我需要mysql使用这个函数,我不想添加一个特定的索引,这就是为什么我不简单地使用DAY和NUMERO的线性函数的原因 下面是一个有序的测试用例:Mysql 列表中按两列排序的前一项,mysql,sql,Mysql,Sql,假设我们有一个包含DAY和NUMERO列的表 可以有许多行具有相同的DAY值,但NUMERO始终是唯一的 在一个按天排序的列表中,最有效的方法是什么,在一个现有的两天之前获取项目 我精确地说,我需要mysql使用这个函数,我不想添加一个特定的索引,这就是为什么我不简单地使用DAY和NUMERO的线性函数的原因 下面是一个有序的测试用例: DAY | NUMERO 1 | 11 1 | 12 1 | 15 4 | 7 4 | 9 4 | 14 5 | 8 6 | 1
DAY | NUMERO
1 | 11
1 | 12
1 | 15
4 | 7
4 | 9
4 | 14
5 | 8
6 | 10
6 | 19
我的请求必须这样做:
1,11=>无
1,15=>1,12
4,7=>1,15
4,9=>4,7
4,14=>4,9
编辑:
我目前的最佳解决方案是进行两次连续查询:
select * from item where day=? and numero<? order by day desc, numero desc limit 1;
select * from item where day<? order by day desc, numero desc limit 1;
如果第一个查询给出结果,我不必运行第二个查询
类似的解决方案是使用一个联合,但mysql似乎并不授权具有多个列的联合
这两种解决方案对于看起来如此简单的问题来说都显得过于沉重…更新
这是一个实际给出正确结果的解决方案
SELECT T1.day,
T1.numero,
COALESCE(MAX(T2.[day]), MAX(T3.[day])) AS prev_day,
COALESCE(MAX(T2.numero), MAX(T3.numero)) AS prev_numero
FROM @table AS T1
LEFT JOIN @table AS T2 ON
(
T2.[day] = T1.[day] AND T2.numero < T1.numero
)
LEFT JOIN @table AS T3 ON
(
T3.[day] < T1.[day]
AND NOT EXISTS(
SELECT *
FROM @table AS T4
WHERE T4.[day] > T3.[day] AND T4.[day] < T1.[day]
)
)
-- Add where clause like so to get specific values
-- WHERE T1.day = 4 AND T1.numero = 7
GROUP BY T1.day, T1.numero
ORDER BY T1.day, T1.numero
更新
这是一个实际给出正确结果的解决方案
SELECT T1.day,
T1.numero,
COALESCE(MAX(T2.[day]), MAX(T3.[day])) AS prev_day,
COALESCE(MAX(T2.numero), MAX(T3.numero)) AS prev_numero
FROM @table AS T1
LEFT JOIN @table AS T2 ON
(
T2.[day] = T1.[day] AND T2.numero < T1.numero
)
LEFT JOIN @table AS T3 ON
(
T3.[day] < T1.[day]
AND NOT EXISTS(
SELECT *
FROM @table AS T4
WHERE T4.[day] > T3.[day] AND T4.[day] < T1.[day]
)
)
-- Add where clause like so to get specific values
-- WHERE T1.day = 4 AND T1.numero = 7
GROUP BY T1.day, T1.numero
ORDER BY T1.day, T1.numero
这是一个普通的查询,避免了CTE、聚合或窗口函数
-- (a Before b) := (a.day < b.day
-- OR (a.day = b.day AND a.numero < b.numero))
SELECT d1.day AS DAY
, d1.numero AS numero
, d0.day AS day0
, d0.numero AS numero0
FROM tmp.lutser d1
LEFT JOIN tmp.lutser d0
ON (d0.day < d1.day OR (d0.day = d1.day AND d0.numero < d1.numero ))
WHERE NOT EXISTS (SELECT *
FROM tmp.lutser d
WHERE (dx.day < d1.day OR (dx.day = d1.day AND dx.numero < d1.numero ))
AND (dx.day > d0.day OR (dx.day = d0.day AND dx.numero > d0.numero ))
)
ORDER BY day,numero
;
结果:
day | numero | day0 | numero0
-----+--------+------+---------
1 | 11 | |
1 | 12 | 1 | 11
1 | 15 | 1 | 12
4 | 7 | 1 | 15
4 | 9 | 4 | 7
4 | 11 | 4 | 9
5 | 8 | 4 | 11
6 | 10 | 5 | 8
6 | 19 | 6 | 10
(9 rows)
这是一个普通的查询,避免了CTE、聚合或窗口函数
-- (a Before b) := (a.day < b.day
-- OR (a.day = b.day AND a.numero < b.numero))
SELECT d1.day AS DAY
, d1.numero AS numero
, d0.day AS day0
, d0.numero AS numero0
FROM tmp.lutser d1
LEFT JOIN tmp.lutser d0
ON (d0.day < d1.day OR (d0.day = d1.day AND d0.numero < d1.numero ))
WHERE NOT EXISTS (SELECT *
FROM tmp.lutser d
WHERE (dx.day < d1.day OR (dx.day = d1.day AND dx.numero < d1.numero ))
AND (dx.day > d0.day OR (dx.day = d0.day AND dx.numero > d0.numero ))
)
ORDER BY day,numero
;
结果:
day | numero | day0 | numero0
-----+--------+------+---------
1 | 11 | |
1 | 12 | 1 | 11
1 | 15 | 1 | 12
4 | 7 | 1 | 15
4 | 9 | 4 | 7
4 | 11 | 4 | 9
5 | 8 | 4 | 11
6 | 10 | 5 | 8
6 | 19 | 6 | 10
(9 rows)
如果您只是基于单个对检索前一对,而不是像其他人提到的那样尝试执行整个表,那么您可以尝试以下简单查询-
:day = 4
:numero = 9
SELECT day, numero
FROM table
WHERE (day = :day AND numero < :numero)
OR (day < :day)
ORDER BY day DESC, numero DESC
LIMIT 1
如果您只是基于单个对检索前一对,而不是像其他人提到的那样尝试执行整个表,那么您可以尝试以下简单查询-
:day = 4
:numero = 9
SELECT day, numero
FROM table
WHERE (day = :day AND numero < :numero)
OR (day < :day)
ORDER BY day DESC, numero DESC
LIMIT 1
好吧,你可以选择当天的maxnumbero,但是numberd比原来的要小。这不会给我4,7中的1,15。有一刻我在思考,我没有看到一个独特的琐碎的问题。我明白了。mysql没有窗口函数?我不这么认为,但我不是mysql或任何DBMS方面的专家,因此我可能缺少一个简单的解决方案。没有窗口函数行数秩或滞后或CTE,就没有简单的解决方案。嗯,你必须构造一组令人讨厌的AND/OR子句。好吧,你可以选择同一天的maxnumbero,但是numberd要比原来的小。这不会给我4,7中的1,15。有一刻我在思考,我没有看到一个独特的琐碎的问题。我明白了。mysql没有窗口函数?我不这么认为,但我不是mysql或任何DBMS方面的专家,因此我可能缺少一个简单的解决方案。没有窗口函数行数秩或滞后或CTE,就没有简单的解决方案。嗯,你必须构造一组讨厌的AND/OR子句。错了。一开始我也是这么想的。参见第二条评论:这不会给我4,7中的1,15,这似乎很有趣,我给你投票,因为我以前不知道coalesce关键字,但我不明白你是如何使用它的。请原谅,如果我是稠密的。获取4,7之前的行的确切请求是什么?@dystroy-docs:@dystroy-要使用它获取特定值,只需添加一个where子句。我更新了我的答案来说明这一点:-其中T1.day=4和T1.numero=7这确实是一个有趣的答案,但我不敢说这是最好的答案,因为即使在第二次尝试时,使用相同的参数,也要慢很多,在一张不到100000行的桌子上,我需要15分钟以上的时间…错了。一开始我也是这么想的。参见第二条评论:这不会给我4,7中的1,15,这似乎很有趣,我给你投票,因为我以前不知道coalesce关键字,但我不明白你是如何使用它的。请原谅,如果我是稠密的。获取4,7之前的行的确切请求是什么?@dystroy-docs:@dystroy-要使用它获取特定值,只需添加一个where子句。我更新了我的答案来说明这一点:-其中T1.day=4和T1.numero=7这确实是一个有趣的答案,但我不敢说这是最好的答案,因为即使在第二次尝试时,使用相同的参数,也要慢很多,因为在一张不到100000行的桌子上,我要花15分钟以上的时间…+1-她很漂亮,我要补充的是,在SQL Server中,至少这一个是一个性能更好的执行工厂。不存在自连接是我最讨厌的事情之一。生成良好的计划,并将在我所知的每个平台上工作,可能是因为习惯用法非常古老/过时。感谢您的见解,但问题与jcooper的答案相同:这是一种缓慢实用的方法。例如,在一个真实数据库上大约100000行的时间是几分钟,而我的dumb two queries解决方案需要几毫秒。dumb two queries只检索1个元组的2个值。库珀和我的检索所有元组。但是你可以在where子句中添加一个限制,当然,正如前面提到的,我想要一个tuple。即使我添加了where子句,它也非常慢。+1-她很漂亮,我至少会在SQL Server中添加它
这是一个性能更好的执行工厂。自我连接不存在是我最讨厌的事情之一。生成良好的计划,并将在我所知的每个平台上工作,可能是因为习惯用法非常古老/过时。感谢您的见解,但问题与jcooper的答案相同:这是一种缓慢实用的方法。例如,在一个真实数据库上大约100000行的时间是几分钟,而我的dumb two queries解决方案需要几毫秒。dumb two queries只检索1个元组的2个值。库珀和我的检索所有元组。但是你可以在where子句中添加一个限制,当然,正如前面提到的,我想要一个tuple。即使我添加了where子句,速度也非常慢。我知道必须有一个简单的解决方案。你知道的。。。我现在觉得自己很笨。。。你赢了。当一个人过度思考问题时,很容易忽视显而易见的东西-我知道必须有一个简单的解决办法。你知道的。。。我现在觉得自己很笨。。。你赢了。当一个人过度思考问题时,很容易忽视显而易见的东西-