具有空值的游标分页(上一个/下一个)
Cursor Pagination (prev / next) with null values
我有一个使用 MySQL(8.0 版)实现的游标分页,只要不涉及 null
值,它就可以正常工作。
这是我的示例数据(id
是随机 UUID,date
是日期,time
是时间):
id | date | time
--------------------------
68 | 2017-10-28 | 22:00:00
d3 | 2017-11-03 | null
dd | 2017-11-03 | 21:45:00
62 | 2017-11-04 | 14:00:00
a1 | 2017-11-04 | 19:40:00
我使用的 cursor
总是包含所有三列。
我使用此查询获取 下一个 结果(在 cursor
之后):
SELECT * FROM table
WHERE (date > cursor.date)
OR (date = cursor.date AND time > cursor.time)
OR (date = cursor.date AND time = cursor.time AND id > cursor.id)
ORDER BY date ASC, time ASC, id ASC
此查询 prev 结果(在 cursor
之前):
SELECT * FROM table
WHERE (date < cursor.date)
OR (date = cursor.date AND time < cursor.time)
OR (date = cursor.date AND time = cursor.time AND id < cursor.id)
ORDER BY date DESC, time DESC, id DESC
当使用 prev 查询和 cursor [id = dd, date = 2017-11-03, time = 21:45:00]
时,它不会 return 带有 id = d3
的行,因为 time
是 null
,time < cursor.time
不会选择它。
虽然我尝试使用 time < cursor.time OR time IS NULL
而不是 time < cursor.time
来包含具有 null
值的行。这似乎解决了这个特定问题,但随后又产生了一个新问题:当使用 prev 查询和 cursor [id = d3, date = 2017-11-03, time = null]
时,因为现在结果包含所提供游标的行。
我希望有一个简单的解决方案。网络上似乎没有处理游标分页中 null
值的示例或教程。
注意: 对于解决方案,null
是否在 non-null
值之前或之后排序并不重要,只要它是一致的。 (MySQL 的默认顺序是 null < non-null
)
我不会触及使用游标进行分页的话题。还有其他选择,例如 limit
/offset
.
但我对您的查询的建议是使用 coalesce()
,为比较分配一个假时间。 MySQL 使这有点简单,因为它支持 time
超过 24 小时的值。对于 date
/time
组合,这些值不是有效值。
所以:
SELECT *
FROM table
WHERE (date > cursor.date) OR
(date = cursor.date AND COALESCE(time, '24:00:00') > COALESCE(cursor.time, '24:00:00')) OR
(date = cursor.date AND COALESCE(time, '24:00:00') = COALESCE(cursor.time, '24:00:00') AND id > cursor.id)
ORDER BY date ASC, time ASC, id ASC
更简洁的 WHERE
子句是:
WHERE (date, COALESCE(time, '24:00:00'), id) > (cursor.date, COALESCE(cursor.time, '24:00:00'), cursor.id)
向 table 添加另一列。将其设为 DATETIME
。不为NULL时将date
和time
合并进去;当 NULL 时,将 date
与某个特定时间结合起来。然后你的光标有两列可以使用并且没有空值。
如果您有相当新的 MySQL 版本,您可以使用“生成的存储”列,从而避免任何代码更改。
并且一定要INDEX(datetime, id)
.
如果您使用的是 MySQL 8.0,那么您可以考虑使用 row_number() window 函数为每一行创建一个唯一的顺序 ID (rn)。然后只需为当前行传递 rn
即可获取前几行。
架构和插入语句:
create table cursortable( id varchar(10), date date, time time);
insert into cursortable values('68' , '2017-10-28' , '22:00:00');
insert into cursortable values('d3' , '2017-11-03' , null);
insert into cursortable values('dd' , '2017-11-03' , '21:45:00');
insert into cursortable values('62' , '2017-11-04' , '14:00:00');
insert into cursortable values('a1' , '2017-11-04' , '19:40:00');
第一次查询得到结果:
select *,row_number()over(order by date,time,id)rn from cursortable
输出:
id
date
time
rn
68
2017-10-28
22:00:00
1
d3
2017-11-03
null
2
dd
2017-11-03
21:45:00
3
62
2017-11-04
14:00:00
4
a1
2017-11-04
19:40:00
5
查询以获取 cursor [id = dd, date = 2017-11-03, time = 21:45:00, rn=3]
的前几行,只有 cursor [rn=3]
:
with cte as
(
select *,row_number()over(order by date,time,id)rn from cursortable
)
select * from cte where rn<3
输出:
id
date
time
rn
68
2017-10-28
22:00:00
1
d3
2017-11-03
null
2
db<>fiddle here
如果您不想在代码中引入计算列,请考虑所有三列尝试以下解决方案 cursor [id = dd, date = 2017-11-03, time = 21:45:00]
查询:
with cte as
(
select *,row_number()over(order by date,time,id)rn from cursortable
)
,cte2 as
(
select * from cte where id='dd' and date= '2017-11-03' and time= '21:45:00'
)
select cte.id,cte.date,cte.time from cte inner join cte2 on cte.rn<cte2.rn
输出:
id
date
time
68
2017-10-28
22:00:00
d3
2017-11-03
null
db<>fiddle here
您的代码如下所示:
with cte as
(
select *,row_number()over(order by date,time,id)rn from cursortable
)
,cte2 as
(
select * from cte where id=cursor.id and date= cursor.date and time= cursor.time
)
select cte.id,cte.date,cte.time from cte inner join cte2 on cte.rn<cte2.rn
我有一个使用 MySQL(8.0 版)实现的游标分页,只要不涉及 null
值,它就可以正常工作。
这是我的示例数据(id
是随机 UUID,date
是日期,time
是时间):
id | date | time
--------------------------
68 | 2017-10-28 | 22:00:00
d3 | 2017-11-03 | null
dd | 2017-11-03 | 21:45:00
62 | 2017-11-04 | 14:00:00
a1 | 2017-11-04 | 19:40:00
我使用的 cursor
总是包含所有三列。
我使用此查询获取 下一个 结果(在 cursor
之后):
SELECT * FROM table
WHERE (date > cursor.date)
OR (date = cursor.date AND time > cursor.time)
OR (date = cursor.date AND time = cursor.time AND id > cursor.id)
ORDER BY date ASC, time ASC, id ASC
此查询 prev 结果(在 cursor
之前):
SELECT * FROM table
WHERE (date < cursor.date)
OR (date = cursor.date AND time < cursor.time)
OR (date = cursor.date AND time = cursor.time AND id < cursor.id)
ORDER BY date DESC, time DESC, id DESC
当使用 prev 查询和 cursor [id = dd, date = 2017-11-03, time = 21:45:00]
时,它不会 return 带有 id = d3
的行,因为 time
是 null
,time < cursor.time
不会选择它。
虽然我尝试使用 time < cursor.time OR time IS NULL
而不是 time < cursor.time
来包含具有 null
值的行。这似乎解决了这个特定问题,但随后又产生了一个新问题:当使用 prev 查询和 cursor [id = d3, date = 2017-11-03, time = null]
时,因为现在结果包含所提供游标的行。
我希望有一个简单的解决方案。网络上似乎没有处理游标分页中 null
值的示例或教程。
注意: 对于解决方案,null
是否在 non-null
值之前或之后排序并不重要,只要它是一致的。 (MySQL 的默认顺序是 null < non-null
)
我不会触及使用游标进行分页的话题。还有其他选择,例如 limit
/offset
.
但我对您的查询的建议是使用 coalesce()
,为比较分配一个假时间。 MySQL 使这有点简单,因为它支持 time
超过 24 小时的值。对于 date
/time
组合,这些值不是有效值。
所以:
SELECT *
FROM table
WHERE (date > cursor.date) OR
(date = cursor.date AND COALESCE(time, '24:00:00') > COALESCE(cursor.time, '24:00:00')) OR
(date = cursor.date AND COALESCE(time, '24:00:00') = COALESCE(cursor.time, '24:00:00') AND id > cursor.id)
ORDER BY date ASC, time ASC, id ASC
更简洁的 WHERE
子句是:
WHERE (date, COALESCE(time, '24:00:00'), id) > (cursor.date, COALESCE(cursor.time, '24:00:00'), cursor.id)
向 table 添加另一列。将其设为 DATETIME
。不为NULL时将date
和time
合并进去;当 NULL 时,将 date
与某个特定时间结合起来。然后你的光标有两列可以使用并且没有空值。
如果您有相当新的 MySQL 版本,您可以使用“生成的存储”列,从而避免任何代码更改。
并且一定要INDEX(datetime, id)
.
如果您使用的是 MySQL 8.0,那么您可以考虑使用 row_number() window 函数为每一行创建一个唯一的顺序 ID (rn)。然后只需为当前行传递 rn
即可获取前几行。
架构和插入语句:
create table cursortable( id varchar(10), date date, time time);
insert into cursortable values('68' , '2017-10-28' , '22:00:00');
insert into cursortable values('d3' , '2017-11-03' , null);
insert into cursortable values('dd' , '2017-11-03' , '21:45:00');
insert into cursortable values('62' , '2017-11-04' , '14:00:00');
insert into cursortable values('a1' , '2017-11-04' , '19:40:00');
第一次查询得到结果:
select *,row_number()over(order by date,time,id)rn from cursortable
输出:
id | date | time | rn |
---|---|---|---|
68 | 2017-10-28 | 22:00:00 | 1 |
d3 | 2017-11-03 | null | 2 |
dd | 2017-11-03 | 21:45:00 | 3 |
62 | 2017-11-04 | 14:00:00 | 4 |
a1 | 2017-11-04 | 19:40:00 | 5 |
查询以获取 cursor [id = dd, date = 2017-11-03, time = 21:45:00, rn=3]
的前几行,只有 cursor [rn=3]
:
with cte as
(
select *,row_number()over(order by date,time,id)rn from cursortable
)
select * from cte where rn<3
输出:
id | date | time | rn |
---|---|---|---|
68 | 2017-10-28 | 22:00:00 | 1 |
d3 | 2017-11-03 | null | 2 |
db<>fiddle here
如果您不想在代码中引入计算列,请考虑所有三列尝试以下解决方案 cursor [id = dd, date = 2017-11-03, time = 21:45:00]
查询:
with cte as
(
select *,row_number()over(order by date,time,id)rn from cursortable
)
,cte2 as
(
select * from cte where id='dd' and date= '2017-11-03' and time= '21:45:00'
)
select cte.id,cte.date,cte.time from cte inner join cte2 on cte.rn<cte2.rn
输出:
id | date | time |
---|---|---|
68 | 2017-10-28 | 22:00:00 |
d3 | 2017-11-03 | null |
db<>fiddle here
您的代码如下所示:
with cte as
(
select *,row_number()over(order by date,time,id)rn from cursortable
)
,cte2 as
(
select * from cte where id=cursor.id and date= cursor.date and time= cursor.time
)
select cte.id,cte.date,cte.time from cte inner join cte2 on cte.rn<cte2.rn