如何按行顺序获取缺失值?

Goo*_*bot 3 mysql insert select

在一个表中,许多行已被删除,如何获取下一个缺失行的 id INSERT

例如

id      col1
1       1
3       3
4       4
5       5
8       8
9       9
Run Code Online (Sandbox Code Playgroud)

我怎样才能获得 next 的第一个可用 id 的值INSERT。在这里,我想获取id = 2.

就像是

SELECT id+1 FROM table WHERE CLAUSE // pointing to the least available value
// or x = id +1 (after SELECT)
INSERT INTO table (id, ....) VALUES ('x', ....)
Run Code Online (Sandbox Code Playgroud)

Dav*_*ett 7

如果该列只是一个代理键并且不用于其他目的(显示排序等),那么我将只使用您的数据库的AUTOINCREMENT等效项并忽略 ID 中存在间隙的事实,因为该点是唯一的而不是服务于其他目的。备受推荐的“SQL Anti-Patterns”一书中有一章题为“pseudokeyneat-freak”。这取决于您正在建模的内容以及当然如何,如果数字确实具有超越标识符的意义,那么这一点是无关紧要的。

您想要阅读的一般概念(值得阅读一些背景知识,因为这些问题随处可见,因此知道如何发现它们并有效地处理它们或将它们设计出来非常有用)是间隙和孤岛。有很多关于此的在线参考资料(这是第一个快速搜索找到的参考资料,它在谈论 MSSQL,但概念是可转移的)。

除了WHERE NOT EXISTS通常建议的方法,因为代码读取的方式会使您的意图更加明显,您还可以执行以下操作:

SELECT TOP 1 t1.id-1
FROM     yourtable t1
LEFT OUTER JOIN 
         yourtable t2 
ON       t2.id=t1.id-1 -- will match if there is a row with the next ID down from the row in t1
WHERE    t2.id IS NULL -- the next ID down not found
AND      t1.id > 0     -- assume 1 is the lowest valid ID
ORDER BY t1.id
Run Code Online (Sandbox Code Playgroud)

(这是 MSSQL 语法,您可能需要对其进行调整)
两种变体都应生成类似的查询计划,因此执行相同,但我已经看到上述方法作为更复杂查询的一部分执行得更好,因此可能值得同时尝试您的情况并验证哪个效果最好。

也不是如果表为空,当前呈现的两种方法都不会返回第一个 ID:您将不会返回任何行(如果您将其用作子查询,则为 NULL)。解决此问题的一种方法是使用“无效”行,id=0但这很脏(您最终必须从许多其他查询中过滤掉它),因此强烈建议改为在逻辑中处理无/NULL。对于在生产中永远不会为空的表(例如,用户表,即使在初始安装时,初始设置/管理员用户也会有一个记录)这或当然不是问题。

编辑:更正查询以在未找到时返回第一个 ID,正如 ypercube 指出的第一个版本不会做

较新的 mySQL 版本

从这个答案开始,mySQL 和 MariaDB 增加了对窗口函数(分别在版本 8 和 10.2 中)的支持,例如LAG()LEAD()。您可以使用这些来检查下一行(或上一行)并比较打算增加的值并查看是否存在间隙,这意味着您不需要将表连接到其自身或EXISTS为此使用子查询比较 - 使用正确的索引,这可以是实现某些间隙/岛查询的更有效的方法。

所有常见的 SQL 数据库引擎(sqlite、postgres、SQL Server、Oracle 等)现在都支持窗口函数。