2015-04-23 4 views
4

У меня есть таблица с данными типа.Выберите минимальное число в диапазоне

ItemCode 
1000 
1002 
1003 
1020 
1060 

Я пытаюсь написать SQL заявление, чтобы получить минимальное количество (Артикул), который не в этой таблице, и он должен быть в состоянии получить следующий наименьший номер после того, как предыдущий минимальный заказ ID был включен в таблице, но также пропустить числа, которые уже находятся в БД. Я хочу получать только 1 результат каждый раз, когда выполняется запрос.

Таким образом, он должен получить 1001 в качестве первого результата, основанного на таблице выше. После того, как ItemCode = 1001 был вставлен в таблицу, следующий результат должен быть равен 1004, поскольку 1000 до 1003 уже существует в таблице.

Основываясь на всем, что я видел в Интернете, я думаю, мне нужно использовать цикл 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

EDIT: Создание новой таблицы не вариант в моем case

Final Редактировать: Спасибо, ребята! Я понял. Вот мой последний запрос, который возвращает именно то, что я хочу. Я знал, что делаю это слишком сложно без причины!

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

Вам не нужно создавать таблицу, вы можете использовать 'CTE' или' sys.columns' в соответствии с решением, предоставляемым wewesthemenace – 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. Проверьте это article от Jeff Moden для справки.

В принципе, вы хотите сгенерировать числа от @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 для создания 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

Вы можете использовать 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; 

EDIT:Here это скрипт SQL, демонстрирующий этот подход. Я не знаю, почему сайт раньше не работал. Кажется, мне почему-то не нравятся мои инструкции declare, поэтому я жестко закодировал границы, но, надеюсь, он все еще получает эту идею.

Смежные вопросы