Как проверить недостающее число из серии номеров?
Я делаю проект, создающий систему приема для колледжа; Технологии - это Java и Oracle.
В одной из таблиц хранятся предварительно сгенерированные серийные номера. Позже, против этих серийных номеров, будут введены данные формы заявки. Мое требование состоит в том, что, когда процесс ввода будет завершен, мне придется генерировать лот-мудрый отчет. Если во время кормления предварительно сгенерированных серийных номеров все номера последовательности исчезли.
Например, скажем в таблице, порядковые номера 7001, 7002, 7004, 7005, 7006, 7010.
Из приведенных выше рядов ясно, что от 7001 до 7010 числа отсутствуют: 7003, 7007, 7008 и 7009
Есть ли какая-либо функция СУБД, доступная в Oracle, чтобы узнать эти числа, или если какая-либо хранимая процедура может выполнить мою задачу, тогда предложите алгоритм.
Я могу найти некоторые методы на Java, но для скорости я хочу найти решение в Oracle.
Ответы
Ответ 1
Решение без жесткого кодирования 9:
select min_a - 1 + level
from ( select min(a) min_a
, max(a) max_a
from test1
)
connect by level <= max_a - min_a + 1
minus
select a
from test1
Результаты:
MIN_A-1+LEVEL
-------------
7003
7007
7008
7009
4 rows selected.
Ответ 2
Попробуйте следующее:
SELECT t1.SequenceNumber + 1 AS "From",
MIN(t2.SequenceNumber) - 1 AS "To"
FROM MyTable t1
JOIN MyTable t2 ON t1.SequenceNumber < t2.SequenceNumber
GROUP BY t1.SequenceNumber
HAVING t1.SequenceNumber + 1 < MIN(t2.SequenceNumber)
Вот результат для последовательности 7001, 7002, 7004, 7005, 7006, 7010:
From To
7003 7003
7007 7009
Ответ 3
Я бы предложил connect by level
как Stefan сделал, однако вы не можете использовать подзапрос в этом утверждении, а это значит, t действительно подходит для вас, так как вам нужно знать, каковы максимальные и минимальные значения вашей последовательности.
Я бы предположил, что строка с табличными функциями может быть лучшим способом для создания чисел, необходимых для соединения. Для этого вам понадобится объект в вашей базе данных, чтобы вернуть значения:
create or replace type t_num_array as table of number;
Тогда функция:
create or replace function generate_serial_nos return t_num_array pipelined is
l_first number;
l_last number;
begin
select min(serial_no), max_serial_no)
into l_first, l_last
from my_table
;
for i in l_first .. l_last loop
pipe row(i);
end loop;
return;
end generate_serial_nos;
/
С помощью этой функции следующее будет возвращать список серийных номеров, между минимальным и максимальным.
select * from table(generate_serial_nos);
Это означает, что ваш запрос, чтобы узнать, какие серийные номера отсутствуют, становится:
select serial_no
from ( select *
from table(generate_serial_nos)
) generator
left outer join my_table actual
on generator.column_value = actual.serial_no
where actual.serial_no is null
Ответ 4
Один простой способ получить ответ для вашего сценария:
create table test1 ( a number(9,0));
insert into test1 values (7001);
insert into test1 values (7002);
insert into test1 values (7004);
insert into test1 values (7005);
insert into test1 values (7006);
insert into test1 values (7010);
commit;
select n.n from (select ROWNUM + 7001 as n from dual connect by level <= 9) n
left join test1 t on n.n = t.a where t.a is null;
Выбор даст вам ответ из вашего примера. Это имеет смысл, если вы заранее знаете, в каком диапазоне ваши номера, и диапазон не должен быть слишком большим. Первое число должно быть смещением в части ROWNUM
, а длина последовательности - это предел уровня в части connect by
.
Ответ 5
Это сработало, но выбирает первую последовательность (начальное значение), поскольку она не имеет предшественника. Протестировано в SQL Server, но должно работать в Oracle
SELECT
s.sequence FROM seqs s
WHERE
s.sequence - (SELECT sequence FROM seqs WHERE sequence = s.sequence-1) IS NULL
Вот результат теста
Table
-------------
7000
7001
7004
7005
7007
7008
Result
----------
7000
7004
7007
Чтобы получить неназначенную последовательность, просто сделайте value[i] - 1
, где я больше первой строки, например. (7004 - 1 = 7003 and 7007 - 1 = 7006)
, которые являются доступными последовательностями
Я думаю, вы можете улучшить этот простой запрос
Ответ 6
Здесь решение, которое:
- Опирается на функцию Oracle LAG
- Не требует знания полной последовательности (но, таким образом, не определяет, были ли пропущены первые или последние числа в последовательности)
- Перечисляет значения, связанные с отсутствующими списками чисел
- Список отсутствующих списков чисел как смежных групп (возможно, удобно для отчетов)
- Трагически сбой для очень больших списков недостающих чисел из-за ограничений listagg
SQL:
WITH MentionedValues /*this would just be your actual table, only defined here to provide data for this example */
AS (SELECT *
FROM ( SELECT LEVEL + 7000 seqnum
FROM DUAL
CONNECT BY LEVEL <= 10000)
WHERE seqnum NOT IN (7003,7007,7008,7009)--omit those four per example
),
Ranges /*identifies all ranges between adjacent rows*/
AS (SELECT seqnum AS seqnum_curr,
LAG (seqnum, 1) OVER (ORDER BY seqnum) AS seqnum_prev,
seqnum - (LAG (seqnum, 1) OVER (ORDER BY seqnum)) AS diff
FROM MentionedValues)
SELECT Ranges.*,
( SELECT LISTAGG (Ranges.seqnum_prev + LEVEL, ',') WITHIN GROUP (ORDER BY 1)
FROM DUAL
CONNECT BY LEVEL < Ranges.diff) "MissingValues" /*count from lower seqnum+1 up to lower_seqnum+(diff-1)*/
FROM Ranges
WHERE diff != 1 /*ignore when diff=1 because that means the numers are sequential without skipping any*/
;
Вывод:
SEQNUM_CURR SEQNUM_PREV DIFF MissingValues
7004 7002 2 "7003"
7010 7006 4 "7007,7008,7009"
Ответ 7
Это работает на postgres >= 8.4. С некоторыми небольшими изменениями в синтаксисе CTE можно было бы работать и для oracle и microsoft.
-- EXPLAIN ANALYZE
WITH missing AS (
WITH RECURSIVE fullhouse AS (
SELECT MIN(num)+1 as num
FROM numbers n0
UNION ALL SELECT 1+ fh0.num AS num
FROM fullhouse fh0
WHERE EXISTS (
SELECT * FROM numbers ex
WHERE ex.num > fh0.num
)
)
SELECT * FROM fullhouse fh1
EXCEPT ( SELECT num FROM numbers nx)
)
SELECT * FROM missing;
Ответ 8
select A.ID + 1 As ID
From [Missing] As A
Where A.ID + 1 Not IN (Select ID from [Missing])
And A.ID < n
Data: ID
1
2
5
7
Result: ID
3
4
6