执行此操作的基本低效方法是获取 ORDERS 中日期在您的目标范围内的每一行的 user_id,并且 ORDERS 中具有相同 user_id 且较低日期的行数正好为 24。
不过,这可能会变得非常丑陋,但很快。
如果你从你控制的代码中调用它,你不能从代码中调用它吗?
如果没有,应该有一种方法为每一行分配一个索引,描述其在特定 user_id 的订单中的排名,并从索引为 25 和正确日期的行中选择所有 user_id。这将为您提供从选择中选择的选择,但它应该更快。这里的难点是控制行的顺序,所以这里是我设想的选择:
- 从将初始化为 0 的两个变量组成的表中选择所有行,按 user_id asc、date asc、union-ed 排序。
- 从此,在更新 var 时全选,以了解一行的 user_id 是否与最后一行相同,并添加一个将报告的字段(因此,对于每个 user_id,按顺序排列的第一行将具有特定值,例如 0而同一 user_id 的其他行将有 1)
- 从这里,如果第一个添加的字段为 1,则选择全部加上等于自身加一的字段,否则为 0
- 由此,从添加的第二个字段为 25 且日期在范围内的行中选择 user_id。
仅当您需要在一个请求中完成所有操作时才需要联合的东西(您必须在比使用它们的选择更低的选择中初始化它们)。
编辑:好吧,如果您也需要日期,您可以将其与 user_id 一起选择,但是在 sql 中计算天数会很麻烦。只需将结果表连接到用户表并获取第 25 次订单的日期和他们的注册日期,您肯定能够在代码中做出差异。
我会尝试构建一个实际的请求,但是如果你想真正了解你需要做什么,你必须阅读 mysql 变量、联合和条件语句。
“看起来太复杂了。我相信这可以通过当前的数据库结构和1-2个请求来完成。”嗯,是的。使用 COUNT 请求,它会很简单,而且速度很慢。
对于复杂的答案,请参阅http://www.sqlfiddle.com/#!9/998358/21
由于您可以使用多个请求,因此您可以先初始化变量。
它实际上并没有那么复杂,您只需要了解如何将“用户的第 25 条命令”具体表达给 SQL 引擎的意思。
查看http://www.sqlfiddle.com/#!9/998358/24了解天数的差异,原来有一种方法。
编辑 5:看来您要使用 COUNT 方法。我会祈祷你的数据库很小。
编辑 6:为后代:
计数方法在非常大的数据库上需要数年时间。由于 OP 没有回来,我假设他的小到足以忽略查询速度。如果那不是您的情况,假设从现在开始 10 年并且 sqlfiddle 链接已失效;这是两个查询的解决方案:
SET @PREV_USR:=0;
SELECT user_id, date_ FROM (
SELECT user_id, date_, SAME_USR AS IGNORE_SMUSR,
@RANK_USR:=(CASE SAME_USR WHEN 0 THEN 1 ELSE @RANK_USR+1 END) AS RANK FROM (
SELECT orders.*, CASE WHEN @PREV_USR = user_id THEN 1 ELSE 0 END AS SAME_USR,
@PREV_USR:=user_id AS IGNORE_USR FROM
orders
ORDER BY user_id ASC, date_ ASC, id ASC
) AS DERIVED_1
) AS DERIVED_2
WHERE RANK = 25 AND YEAR(date_) = 2019 AND MONTH(date_) = 4 ;
只需更改 RANK = ?以及满足您需求的条件。如果您想完全理解它,请从最里面的 SELECT 开始,然后再往高处走;这个版本融合了我解释的第 1 点和第 2 点。
现在有时您必须使用 API 或其他东西,除非您提交它或其他一些限制,否则它不会让您将变量值保存在内存中,并且您需要在一个查询中完成。为此,您将初始化降低了一步,并使其不会影响更高的语句。 IMO 最好的方法是在一个带有假表的 UNION 中,其中唯一的行被排除在外。您将避免 JOIN 的麻烦,而且整体效果更好。
SELECT user_id, date_ FROM (
SELECT user_id, date_, SAME_USR AS IGNORE_SMUSR,
@RANK_USR:=(CASE SAME_USR WHEN 0 THEN 1 ELSE @RANK_USR+1 END) AS RANK FROM (
SELECT DERIVED_4.*, CASE WHEN @PREV_USR = user_id THEN 1 ELSE 0 END AS SAME_USR,
@PREV_USR:=user_id AS IGNORE_USR FROM
(SELECT * FROM orders
UNION
SELECT * FROM (
SELECT (@PREV_USR:=0) AS INIT_PREV_USR, 0 AS COL_2, 0 AS COL_3
) AS DERIVED_3
WHERE INIT_PREV_USR <> 0
) AS DERIVED_4
ORDER BY user_id ASC, date_ ASC, id ASC
) AS DERIVED_1
) AS DERIVED_2
WHERE RANK = 25 AND YEAR(date_) = 2019 AND MONTH(date_) = 4 ;
使用该方法,需要注意的是基本表中列的数量和类型。这里订单的第一个字段是一个 int,所以我首先将 INIT_PREV_USR 放入,然后还有两个字段,所以我只需添加两个带名称的零,然后就可以结束了。大多数类型都有效,因为联合实际上并没有做任何事情,但是当您的第一个字段是 blob 时,我不会尝试这样做(最坏的情况是您可以使用 JOIN)。
您会注意到这是源自 mysql 中的分页方法。如果您想将此应用到其他引擎,只需查看他们最好的分页调用,您应该能够进行思考。