mysql 游标分页(上一页/下一页),具有空值

m528fe3b  于 2023-08-02  发布在  Mysql
关注(0)|答案(5)|浏览(124)

我用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总是由所有三列组成。
我使用这个查询来获取next结果(在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


当对cursor [id = dd, date = 2017-11-03, time = 21:45:00]使用prev查询时,它不会返回带有id = d3的行,因为timenull,而这不会被time < cursor.time选中。
尽管我尝试使用time < cursor.time OR time IS NULL而不是time < cursor.time来包含具有null值的行。这似乎解决了这个特定的问题,但随后又产生了一个新问题:当使用cursor [id = d3, date = 2017-11-03, time = null]prev查询时,因为现在结果包含所提供游标的行。
我希望有一个简单的解决方案。网络上似乎没有处理光标分页中null值的示例或教程。

**注意:**对于解决方案,null是否在non-null值之前或之后排序并不重要,只要它是一致的。(MySQL的默认排序是null < non-null

ctzwtxfj

ctzwtxfj1#

我有点迟到了,但给予下面的方法吧。游标逻辑需要针对可为空的列进行调整,以及游标行为time列返回空值时进行调整。
下面列出了两个示例:

declare @cursorID nvarchar(2)
declare @cursorDate date
declare @cursorTime time(0)

declare @table table(id nvarchar(2), date date, time time(0))
insert into @table
values
('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')

--IF SELECTING A ROW WITH A NON-NULL VALUE FOR TIME
set @cursorID = 'dd'
set @cursorDate = (select date from @table where id = @cursorID)
set @cursorTime = (select time from @table where id = @cursorID)

--ASCENDING CURSOR VALUES
select * from @table
where
date >= @cursorDate
and (date > @cursorDate or (time >= @cursorTime
and (time > @cursorTime or id > @cursorID)))
order by
date asc, time asc, id asc

--DESCENDING CURSOR VALUES
select * from @table
where
date <= @cursorDate
and (date < @cursorDate or ((time <= @cursorTime or time is null)
and ((time < @cursorTime or time is null) or id < @cursorID)))
order by
date desc, time desc, id desc

--IF SELECTING A ROW WITH A NON VALUE FOR TIME
set @cursorID = 'd3'
set @cursorDate = (select date from @table where id = @cursorID)
set @cursorTime = (select time from @table where id = @cursorID)

--ASCENDING CURSOR VALUES
select * from @table
where
date >= @cursorDate
and (date > @cursorDate or (time is not null
or (time is null and id > @cursorID)))
order by
date asc, time asc, id asc

--DESCENDING CURSOR VALUES
select * from @table
where
date <= @cursorDate
and (date < @cursorDate or (time is null
and (id < @cursorID)))
order by
date desc, time desc, id desc

字符串

mklgxw1f

mklgxw1f2#

向表中添加另一列。把它设为DATETIME。当不为NULL时,将datetime合并到其中;当NULL时,将date与某个特定时间组合。这样,您的游标就有两列要处理,而且没有空值。
如果你有一个合理的最新版本的MySQL,你可以使用一个“生成的存储”列,从而避免任何代码更改。
而且一定要有INDEX(datetime, id)

vql8enpb

vql8enpb3#

我不打算触及使用光标进行分页的主题。有一些替代方案,例如limit/offset
但我建议您使用coalesce(),为比较分配一个假时间。MySQL使这变得有些简单,因为它支持超过24小时的time值。对于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)

wztqucjr

wztqucjr4#

如果你使用的是MySQL 8.0,那么你可以考虑使用row_number()窗口函数为每一行创建一个唯一的序列ID(rn)。然后只传递当前行的rn以获取前一行。
Schema和insert语句:

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


输出量:
| 日期,日期|计时器|rn| rn |
| --|--|--| ------------ |
| 2017-10-28 2017-10-28| 22点|一个| 1 |
| 2017-11-03 2017-11-03|零|二个| 2 |
| 2017-11-03 2017-11-03| 21点45分|三个| 3 |
| 2017-11-04 2017-11-04|下午4点|四个| 4 |
| 2017-11-04 2017-11-04| 19点40分|五个| 5 |
查询以获取仅包含cursor [rn=3]cursor [id = dd, date = 2017-11-03, time = 21:45:00, rn=3]的前几行:

with cte as
 (
   select *,row_number()over(order by date,time,id)rn from cursortable
 )
 select * from cte where rn<3


输出量:
| 日期,日期|计时器|rn| rn |
| --|--|--| ------------ |
| 2017-10-28 2017-10-28| 22点|一个| 1 |
| 2017-11-03 2017-11-03| * 空 *| 二个| 2 |

  • db<>小提琴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


输出量:
| 日期,日期|计时器| time |
| --|--| ------------ |
| 2017-10-28 2017-10-28| 22点| 22:00:00 |
| 2017-11-03 2017-11-03| * 空 *| null |

你的代码应该像下面这样:

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

jq6vz3qz

jq6vz3qz5#

我的解决方案是将我想用作分页游标的可空列转换为具有默认值的NOT NULL列。
对于文本列,我将默认值设置为空字符串,这使得所有这些记录都出现在升序排序的顶部,对于我的日期时间,我将默认值设置为最早的可能值:“1970-01-01 00:00:00”,这使得它们出现在降序排序的末尾。
然后,在TS/JS中检查应用中的日期时间“空”时,它就像这样简单:

const isNullDate = someDate <= new Date(0)

字符串
如果您的系统依赖于这些空值,那么这对您来说不是正确的解决方案,但在我的情况下,这是最简单/优雅/减少头痛的解决方案。

相关问题