2015-04-23 52 views
4

我有一个类似数据的表。选择一个范围内的最小数量

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 

我觉得有一个更简单的方法来实现这一点。有没有办法让我说......

选择未在表中存在1000和1060之间的最小数量的X

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

最后编辑:谢谢你们!我知道了。这是我最后的查询,返回我想要的。我知道我没有理由让它变得太复杂了!

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

您不需要创建表,您可以根据wewesthemenace提供的解决方案使用'CTE'或'sys.columns' – ughai

回答

1

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

您可以使用Tally表完成此操作。查看Jeff Moden的article以供参考。

基本上,您要生成从@start@end的数字。这就是Tally表的来源,它将用于数字生成。当你有你的号码时,你只需要检查表中不存在的最小值。

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产生理货表另一个查询:

;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

你可以使用row_number()产生连续值的每一行,再看看对于row_number()与表中存储的值不匹配的第一行。我的SQL Server安装此时不工作,SQL小提琴似乎太过了,所以我写了这个,而不能对其进行测试,但这样的事情应该工作:

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陈述出于某种原因,所以我硬编码的边界,而是希望它仍然能够通过这个想法。