Select 范围内的最小数量

Select minimum number in a range

我有一个 table 数据类似。

ItemCode
1000
1002
1003
1020
1060

我正在尝试编写一个 SQL 语句来获取不在此 table 中的最小编号 (ItemCode) 并且它应该能够在上一个已在 table 中插入最小订单 ID,但也会跳过数据库中已有的数字。每次查询 运行.

我只想得到 1 个结果

所以,在上面的table的基础上,应该得到1001作为第一个结果。一旦 ItemCode = 1001 被插入到 table 中,它应该得到的下一个结果应该是 1004 因为 10001003 已经存在于 table.

根据我在网上看到的所有内容,我想,我必须使用 While 循环来执行此操作。这是我仍在处理的代码。

DECLARE @Count int
SET @Count= 0   
WHILE Exists (Select ItemCode
                from OITM
                where itemCode like '10%'
                AND convert(int,ItemCode) >= '1000'
                and convert(int,ItemCode) <= '1060')
        Begin
            SET @COUNT = @COUNT + 1

            select MIN(ItemCode) + @Count
            from OITM
            where itemCode like '10%'
            AND convert(int,ItemCode) >= '1000'
            and convert(int,ItemCode) <= '1060'
        END

我觉得必须有一种更简单的方法来完成此任务。有没有办法让我说...

select table X

中不存在的 1000 到 1060 之间的最小数字

编辑:创建一个新的 table 不是我的选择

最终编辑:谢谢大家!我知道了。这是我的最终查询 returns 正是我想要的。我知道我无缘无故地把它弄得太复杂了!

With T0 as ( select convert(int,ItemCode) + row_number() over (order by convert(int,ItemCode)) as ItemCode
             from OITM
             where itemCode like '10%'
             AND convert(int,ItemCode) >= '1000'
             And convert(int,ItemCode) <= '1060')
Select MIN(convert(varchar,ItemCode)) as ItemCode
from T0
where convert(int,ItemCode) Not in (Select convert(int,ItemCode)
                                    from OITM
                                    where itemCode like '10%'
                                    AND convert(int,ItemCode) >= '1000'
                                    and convert(int,ItemCode) <= '1060');

您可以使用 Tally Table 来做到这一点。查看 Jeff Moden 的 article 以供参考。

基本上,您想要生成从 @start@end 的数字。这就是 Tally Table 的用武之地。它将用于数字生成。有了数字后,您只需检查 table.

中不存在的最小值

SQL Fiddle

DECLARE @start INT = 1000
DECLARE @end   INT = 1060

;WITH E1(N) AS(
    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 
)
,E2(N) AS(SELECT 1 FROM E1 a, E1 b)
,E4(N) AS(SELECT 1 FROM E2 a, E2 b)
,Tally(N) AS(
    SELECT TOP(@end - @start + 1) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) - 1 FROM E4
)
SELECT
    MIN(@start + t.N)
FROM Tally t
WHERE
    @start + t.N <= @end
    AND NOT EXISTS(
        SELECT 1 
        FROM OITM
        WHERE CAST(ItemCode AS INT) = @start + t.N
    )

这是另一个使用 sys.columns 生成 Tally Table 的查询:

;WITH Tally(N) AS(
    SELECT TOP(@end - @start + 1) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) - 1 
    FROM sys.columns a
    --CROSS JOIN sys.columns b
)
SELECT
    MIN(@start + t.N)
FROM Tally t
WHERE
    @start + t.N <= @end
    AND NOT EXISTS(
        SELECT 1 
        FROM OITM
        WHERE CAST(ItemCode AS INT) = @start + t.N
    )

您可以使用 row_number() 为每一行生成顺序值,然后查找 row_number() 与存储在 table 中的值不匹配的第一行。我的 SQL 服务器安装目前不工作,SQL Fiddle 似乎也出现故障,所以我写了这篇文章但无法测试它,但像这样的东西应该可以工作:

declare @lowerBound int = 1000;
declare @upperBound int = 1060;

declare @x table ([id] int);
insert @x values (1000), (1002), (1003), (1020), (1060);

with [SequenceCTE] as
(
    select
        [id],
        [seq] = (@lowerBound - 1) + row_number() over (order by [id])
    from
        @x
)
select top 1
    [seq]
from
    [SequenceCTE]
where
    [seq] != [id] and
    [seq] <= @upperBound;

编辑: Here 是演示此方法的 SQL Fiddle。我不知道为什么该网站以前不适合我。由于某种原因,它似乎不喜欢我的 declare 语句,所以我改为硬编码边界,​​但希望它仍然能理解这个想法。

这应该可以做到。在这里,您为行生成序列号,然后将每一行与下一行进行比较(通过连接条件完成),并仅过滤差异不为 1 的那些行,按顺序排序,最后选择最上面的行。

;with c as(select id, row_number() over(order by id) rn)
select top 1 c1.id + 1 as NewID
from c as c1
join c as c2 on c1.rn + 1 = c2.rn
where c2.id - c1.id <> 1
order by c1.rn