SQL查找序列中的缺失值

工作中遇到这样一个场景,接入的数据源需要在内网集群中映射一个可访问的端口,端口有申请也会有释放,既要保障申请的端口不冲突,也要能让释放的端口再利用。

通常的设计方式肯定是每分配一个端口就向数据库表里插入一条记录,那么查询可申请的端口号就可以抽象成一个查找序列缺失值的问题。

还有另一种反向记录的思路,就是先把所有可申请的端口号记录插入表里,端口号最多也就几万条记录。
每分配一个端口就删掉一条记录,这样的做法查询效率更好,但分配的端口还需要记录其他配置信息,就需要准备两张表。


想到一种通过SQL查找序列缺失值的方法,是将表的序列值-1再与原表做join,如果能够匹配上说明该序列值的下一个值是存在的,如果没有匹配上那就说明下一个值在表里不存在,正是我们要找的缺失值。

比如表中有5条记录,1、2、3、5、6,-1后的记录为0、1、2、4、5。两个表做left join后,3和6没有匹配上,可以找到3和6的下一个值是缺失的,+1就可以得到4和7。

join on t1.num = t2.num - 1
t1.numt2.num
12
23
3NULL
56
6NULL

MySQL中建表和示例SQL如下:

mysql> create table seq(num int);
Query OK, 0 rows affected (0.03 sec)

mysql> insert into seq values(1),(2),(3),(4),(5),(6),(8),(9),(10);
Query OK, 9 rows affected (0.01 sec)
Records: 9  Duplicates: 0  Warnings: 0

mysql> select s1.num n1, s2.num n2 from seq s1 left join seq s2 on s1.num = s2.num - 1;
+------+------+
| n1   | n2   |
+------+------+
|    1 |    2 |
|    2 |    3 |
|    3 |    4 |
|    4 |    5 |
|    5 |    6 |
|    6 | NULL |
|    8 |    9 |
|    9 |   10 |
|   10 | NULL |
+------+------+
9 rows in set (0.01 sec)

mysql> select s1.num + 1 from seq s1 left join seq s2 on s1.num = s2.num - 1 where s1.num > 0 and s2.num is null order by s1.num asc limit 1;
+------------+
| s1.num + 1 |
+------------+
|          7 |
+------------+
1 row in set (0.00 sec)