MySQL 中自定义排序的累计总和
Cumulative sum with custom sorting in MySQL
我有一个 table 看起来像这样
id remaining expiry_date
1 200 2019-11-15
2 10 2019-11-23
3 10 2019-11-16
4 10 2019-11-16
5 7 2019-11-16
我想获取 运行 总共 215 个结果,按 expiry_date
ascending
顺序排序。
到目前为止我能取得什么成就?
SELECT *, @sum := (@sum + remaining) AS csum
FROM tickets
JOIN (SELECT @sum := 0) r
WHERE @sum < 215
ORDER BY id;
本次查询return以下结果正确。
id remaining expiry_date csum
1 200 2019-11-15 200
2 10 2019-11-23 210
3 10 2019-11-16 220
但是当我尝试用 expiry_date
对它进行排序时,它 return 包含所有记录。
SELECT *, @sum := (@sum + remaining) AS csum
FROM tickets
JOIN (SELECT @sum := 0) r
WHERE @sum < 215
ORDER BY expiry_date;
结果:
id remaining expiry_date csum
1 200 2019-11-15 200
3 10 2019-11-16 210
4 10 2019-11-16 220
5 7 2019-11-16 227
2 10 2019-11-23 237
排序正确,但结果超出我的需要。
我想要什么
我要return下面的结果。
id remaining expiry_date csum
1 200 2019-11-15 200
3 10 2019-11-16 210
4 10 2019-11-16 220
此外,数字 215 可以动态变化,因此行数 returned 可以根据该数字变化。我如何更改查询才能实现此目的?
编辑
对于我在结果集中实际想要的内容不清楚,我深表歉意。请让我澄清一下这个编辑。我不想要 运行-total 少于给定数量的记录。我想要记录,直到 运行-总数等于或超过给定的数量。
尝试使用限制 3 并将此结果作为新订单的子查询:
SELECT *
FROM
(SELECT *, @sum := (@sum + remaining) AS csum
FROM tickets
JOIN (SELECT @sum := 0) r
WHERE @sum < 215
ORDER BY id
LIMIT 3) t
ORDER BY expiry_date
或者根据您更新的问题,您可能只需要按日期排序的最后 3 个
SELECT *, @sum := (@sum + remaining) AS csum
FROM tickets
JOIN (SELECT @sum := 0) r
WHERE @sum < 215
ORDER BY expiry_date
LIMIT 3;
否则,如果您不想使用限制但想过滤 csum 的结果,那么您可以尝试将您的查询用作子查询并过滤您想要的值,例如:225
SELECT *
FROM
(SELECT t.*, @sum := (@sum + t.remaining) AS csum
FROM tickets t
JOIN (SELECT @sum := 0) r
ORDER BY expiry_date ) t1
WHERE t1.csum < 225
勾选
SELECT *
FROM
(SELECT t.*, @sum := (@sum + t.remaining) AS csum
FROM
(SELECT 1 id, 200 remaining, '2019-11-15' expiry_date
UNION ALL
SELECT 2, 10, '2019-11-23'
UNION ALL
SELECT 3, 10, '2019-11-16'
UNION ALL
SELECT 4, 10, '2019-11-16'
UNION ALL
SELECT 5, 7, '2019-11-16') t
JOIN (SELECT @sum := 0) r
ORDER BY expiry_date ) t1
WHERE t1.csum < 225
首先每个日期有多个条目。因此,单靠日期不足以获得稳定的排序顺序。我建议 ORDER BY expiry_date, id
弄清楚这一点。
然后,运行 总数将在任何现代 RDBMS 中使用 window 函数完成。从版本 8 开始,它们在 MySQL 中可用。
select id, remaining, expiry_date, csum
from
(
select
id, remaining, expiry_date,
sum(remaining) over (order by expiry_date, id) as csum,
sum(remaining) over (order by expiry_date, id
rows between unbounded preceding and 1 preceding) as lag_csum
from tickets
) summed
where coalesce(lag_csum, 0) < 215
order by expiry_date, id;
如果 window 函数不可用,您可以改用相关聚合子查询。这可能要慢得多,但应该也能正常工作。
select *
from
(
select
id, remaining, expiry_date,
(
select sum(remaining)
from tickets t2
where t2.expiry_date < t1.expiry_date
or (t2.expiry_date = t1.expiry_date and t2.id <= t1.id)
) as csum,
(
select sum(remaining)
from tickets t2
where t2.expiry_date < t1.expiry_date
or (t2.expiry_date = t1.expiry_date and t2.id < t1.id)
) as lag_csum
from tickets t1
) summed
where coalesce(lag_csum, 0) < 215
order by expiry_date, id;
两个查询都是标准的 SQL,因此不限于 MySQL。
我有一个 table 看起来像这样
id remaining expiry_date
1 200 2019-11-15
2 10 2019-11-23
3 10 2019-11-16
4 10 2019-11-16
5 7 2019-11-16
我想获取 运行 总共 215 个结果,按 expiry_date
ascending
顺序排序。
到目前为止我能取得什么成就?
SELECT *, @sum := (@sum + remaining) AS csum
FROM tickets
JOIN (SELECT @sum := 0) r
WHERE @sum < 215
ORDER BY id;
本次查询return以下结果正确。
id remaining expiry_date csum
1 200 2019-11-15 200
2 10 2019-11-23 210
3 10 2019-11-16 220
但是当我尝试用 expiry_date
对它进行排序时,它 return 包含所有记录。
SELECT *, @sum := (@sum + remaining) AS csum
FROM tickets
JOIN (SELECT @sum := 0) r
WHERE @sum < 215
ORDER BY expiry_date;
结果:
id remaining expiry_date csum
1 200 2019-11-15 200
3 10 2019-11-16 210
4 10 2019-11-16 220
5 7 2019-11-16 227
2 10 2019-11-23 237
排序正确,但结果超出我的需要。
我想要什么
我要return下面的结果。
id remaining expiry_date csum
1 200 2019-11-15 200
3 10 2019-11-16 210
4 10 2019-11-16 220
此外,数字 215 可以动态变化,因此行数 returned 可以根据该数字变化。我如何更改查询才能实现此目的?
编辑
对于我在结果集中实际想要的内容不清楚,我深表歉意。请让我澄清一下这个编辑。我不想要 运行-total 少于给定数量的记录。我想要记录,直到 运行-总数等于或超过给定的数量。
尝试使用限制 3 并将此结果作为新订单的子查询:
SELECT *
FROM
(SELECT *, @sum := (@sum + remaining) AS csum
FROM tickets
JOIN (SELECT @sum := 0) r
WHERE @sum < 215
ORDER BY id
LIMIT 3) t
ORDER BY expiry_date
或者根据您更新的问题,您可能只需要按日期排序的最后 3 个
SELECT *, @sum := (@sum + remaining) AS csum
FROM tickets
JOIN (SELECT @sum := 0) r
WHERE @sum < 215
ORDER BY expiry_date
LIMIT 3;
否则,如果您不想使用限制但想过滤 csum 的结果,那么您可以尝试将您的查询用作子查询并过滤您想要的值,例如:225
SELECT *
FROM
(SELECT t.*, @sum := (@sum + t.remaining) AS csum
FROM tickets t
JOIN (SELECT @sum := 0) r
ORDER BY expiry_date ) t1
WHERE t1.csum < 225
勾选
SELECT *
FROM
(SELECT t.*, @sum := (@sum + t.remaining) AS csum
FROM
(SELECT 1 id, 200 remaining, '2019-11-15' expiry_date
UNION ALL
SELECT 2, 10, '2019-11-23'
UNION ALL
SELECT 3, 10, '2019-11-16'
UNION ALL
SELECT 4, 10, '2019-11-16'
UNION ALL
SELECT 5, 7, '2019-11-16') t
JOIN (SELECT @sum := 0) r
ORDER BY expiry_date ) t1
WHERE t1.csum < 225
首先每个日期有多个条目。因此,单靠日期不足以获得稳定的排序顺序。我建议 ORDER BY expiry_date, id
弄清楚这一点。
然后,运行 总数将在任何现代 RDBMS 中使用 window 函数完成。从版本 8 开始,它们在 MySQL 中可用。
select id, remaining, expiry_date, csum
from
(
select
id, remaining, expiry_date,
sum(remaining) over (order by expiry_date, id) as csum,
sum(remaining) over (order by expiry_date, id
rows between unbounded preceding and 1 preceding) as lag_csum
from tickets
) summed
where coalesce(lag_csum, 0) < 215
order by expiry_date, id;
如果 window 函数不可用,您可以改用相关聚合子查询。这可能要慢得多,但应该也能正常工作。
select *
from
(
select
id, remaining, expiry_date,
(
select sum(remaining)
from tickets t2
where t2.expiry_date < t1.expiry_date
or (t2.expiry_date = t1.expiry_date and t2.id <= t1.id)
) as csum,
(
select sum(remaining)
from tickets t2
where t2.expiry_date < t1.expiry_date
or (t2.expiry_date = t1.expiry_date and t2.id < t1.id)
) as lag_csum
from tickets t1
) summed
where coalesce(lag_csum, 0) < 215
order by expiry_date, id;
两个查询都是标准的 SQL,因此不限于 MySQL。