选择范围中的最小数量

时间:2015-04-23 02:54:44

标签: sql sql-server rdbms

我有一个包含数据的表格。

ItemCode
1000
1002
1003
1020
1060

我正在尝试编写一个SQL语句来获取该表中不存在的最小数字(ItemCode),并且一旦在表中插入了先前的最小订单ID,它应该能够获得下一个最小数字但是也跳过数据库中已有的数字。我只希望每次运行查询时得到1个结果。

因此,它应该得到1001作为基于上表的第一个结果。将ItemCode = 1001插入表格后,其应获得的下一个结果应为1004,因为表格中已存在10001003

根据我在网上看到的所有内容,我认为,我必须使用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

我觉得必须有一种更简单的方法来实现这一目标。有没有办法让我说...

选择表X中不存在的1000到1060之间的最小数字

编辑:在我的案例中,创建一个新表不是一个选项

最终编辑:谢谢你们!我知道了。这是我的最终查询,它返回我想要的内容。我知道我无缘无故地让它太复杂了!

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');

3 个答案:

答案 0 :(得分:1)

您可以使用Tally Table执行此操作。请查看Jeff Moden的article作为参考。

基本上,您希望生成从@start@end的数字。这就是Tally 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表的查询:

;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
    )

答案 1 :(得分:1)

您可以使用row_number()为每行生成连续值,然后查找row_number()与表中存储的值不匹配的第一行。我的SQL Server安装目前还没有工作,而且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小提琴。我不知道为什么该网站之前没有为我工作。由于某些原因,它似乎不喜欢我的declare语句,所以我改为编码边界,​​但希望它仍然可以得到这个想法。

答案 2 :(得分:1)

这应该做的事情。在这里,您将为行生成sequantial数字,然后将每行与下一行(通过连接条件完成)进行比较,并仅在差异不为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