【问题标题】:How to get the first available number in a range如何获取范围内的第一个可用数字
【发布时间】:2022-01-12 03:57:59
【问题描述】:

我需要什么

给定一组从1 到极限n 的数字,获取最小的未被另一条记录占用的数字

详情

我的任务是将一些客户信息上传到数据库中;它需要支持遗留功能,所以我无法控制它的结构。

有一列“代码”是INT,UNIQUE,已经插入了一些数据,但它不是连续的,例如它可以有以下记录(1,2,3,5,7,125,200,500,127000)

我需要在每条新记录中插入一个尚不存在的数字,但我不想在数据中留下“漏洞”。

我不能简单地选择最大的一个并添加一个,因为最大的一个可能非常接近列的限制,而两者之间有很多可用的数字。而且我需要插入数千条记录。

例如,对于上面示例中的记录,此函数将返回“4”,因为“1”、“2”和“3”已被使用。然后,如果我使用“4”插入一条记录,它将返回“6”,因为“5”已经存在(所以请注意,我不能使用最后插入的并添加一个,因为下一个也可能被使用)...等等

我的尝试

我实际上是使用以下登录名完成的(我编写 伪代码 来简化查询,因为查询包含许多列,这使得查询非常长)

-- for each record
DECLARE i = 0
WHILE i < limit
    IF (COUNT(*) FROM MyTable WHERE Code = i) = 0
        INSERT INTO MyTable (Code, ...) VALUES (i, ...)
        BREAK
    SET i = i+1;

这行得通,但是使查询非常长并且效率不高,就好像我有 1000 次插入和从 1 到 500 的记录将循环 500000 次一样。有没有更好的方法来做到这一点?

类似INSERT INTO MyTable (Code, ...) VALUES (SMALLEST_AVALIABLE(Code), ...)

非常感谢您的宝贵时间!

【问题讨论】:

  • 虽然@DaleK 是您需要的答案,但我在想您为什么要填补数据库中的漏洞?它假设您不这样做,并且是数据库上的常见行为。
  • 我使用的列有一组非常有限的可用数字(大约 100 个),所以如果我想插入 80 而不使用整体,我将无法做到。谢谢@DaleK 我会看看你发送的链接。
  • 为了遵循这个请求,您需要消除并发事务(查询)的基本概念。例如,您可以从其他事务中锁定该表,如果它们同时执行,这些事务可能会获得相同的值,但是由于执行此请求,您将在数据库中锁定和等待。此外,由于您在列上使用 UNIQUE,因此如果您不实现同步使用,那么在进行并发插入时可能会出错。
  • "it needs to support legacy features so I don't have control over it's structure.":这是最常见的借口,也是大多数情况下最糟糕的借口——如果你负责系统,那么控制,如果你不在在系统上收费,然后将任务传递给他。很多时候重新设计系统就是正确的解决方案。

标签: sql sql-server tsql


【解决方案1】:

如何通过查询来做到这一点:

with range as (
  select min(code) as min, max(code) as max from MyTable
), nums as (
    select min as x from range
    union all
    select x + 1 from nums 
    where x < (select max from range)
)
insert into MyTable (code)
select x from nums
left join MyTable on code = x
where code is null
option (maxrecursion 0)

live demo

注意:这不是那么有效,除了编码器。

【讨论】:

  • 在我看来,使用循环(递归)创建数字表是没有意义的,尤其是当您在每个查询中即时使用它时。您应该在系统中有一个数字表,即使您想即时创建一个数字表,也有更好的选项,例如使用 ROW_NUMBR 和其他选项从 sys.all_objects 中选择
  • @RonenAriely 毫无意义的是,SQL Server 与诸如 postgres 之类的开源数据库相比落后了多远,后者通过generate_series()为这个问题提供了一个简单的解决方案
  • 谢谢,这将最终完成这项工作。我不关心效率,因为数据加载一次,然后大多只读取。非常感谢!
猜你喜欢
  • 1970-01-01
  • 2016-07-07
  • 1970-01-01
  • 2019-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-06
  • 2021-07-24
相关资源
最近更新 更多