Вот более надежная процедура для изменения следующего значения, возвращаемого последовательностью, плюс намного больше.
- Во-первых, он защищает от атак SQL-инъекций, поскольку ни одна из переданных строк не используется для непосредственного создания каких-либо динамических операторов SQL,
- Во-вторых, он предотвращает установку следующего значения последовательности за пределами минимального или максимального значения последовательности.
next_value
будет! = min_value
и между min_value
и max_value
.
- В-третьих, при очистке учитывается текущая (или предполагаемая) настройка
increment_by
, а также все остальные настройки последовательности.
- В-четвертых, все параметры, кроме первого, являются необязательными и, если не указано, принимают текущие настройки последовательности в качестве значений по умолчанию. Если дополнительные параметры не указаны, никаких действий не предпринимается.
- Наконец, если вы попытаетесь изменить последовательность, которая не существует (или не принадлежит текущему пользователю), это вызовет ошибку
ORA-01403: no data found
.
Вот код:
CREATE OR REPLACE PROCEDURE alter_sequence(
seq_name user_sequences.sequence_name%TYPE
, next_value user_sequences.last_number%TYPE := null
, increment_by user_sequences.increment_by%TYPE := null
, min_value user_sequences.min_value%TYPE := null
, max_value user_sequences.max_value%TYPE := null
, cycle_flag user_sequences.cycle_flag%TYPE := null
, cache_size user_sequences.cache_size%TYPE := null
, order_flag user_sequences.order_flag%TYPE := null)
AUTHID CURRENT_USER
AS
l_seq user_sequences%rowtype;
l_old_cache user_sequences.cache_size%TYPE;
l_next user_sequences.min_value%TYPE;
BEGIN
-- Get current sequence settings as defaults
SELECT * INTO l_seq FROM user_sequences WHERE sequence_name = seq_name;
-- Update target settings
l_old_cache := l_seq.cache_size;
l_seq.increment_by := nvl(increment_by, l_seq.increment_by);
l_seq.min_value := nvl(min_value, l_seq.min_value);
l_seq.max_value := nvl(max_value, l_seq.max_value);
l_seq.cycle_flag := nvl(cycle_flag, l_seq.cycle_flag);
l_seq.cache_size := nvl(cache_size, l_seq.cache_size);
l_seq.order_flag := nvl(order_flag, l_seq.order_flag);
IF next_value is NOT NULL THEN
-- Determine next value without exceeding limits
l_next := LEAST(GREATEST(next_value, l_seq.min_value+1),l_seq.max_value);
-- Grab the actual latest seq number
EXECUTE IMMEDIATE
'ALTER SEQUENCE '||l_seq.sequence_name
|| ' INCREMENT BY 1'
|| ' MINVALUE '||least(l_seq.min_value,l_seq.last_number-l_old_cache)
|| ' MAXVALUE '||greatest(l_seq.max_value,l_seq.last_number)
|| ' NOCACHE'
|| ' ORDER';
EXECUTE IMMEDIATE
'SELECT '||l_seq.sequence_name||'.NEXTVAL FROM DUAL'
INTO l_seq.last_number;
l_next := l_next-l_seq.last_number-1;
-- Reset the sequence number
IF l_next <> 0 THEN
EXECUTE IMMEDIATE
'ALTER SEQUENCE '||l_seq.sequence_name
|| ' INCREMENT BY '||l_next
|| ' MINVALUE '||least(l_seq.min_value,l_seq.last_number)
|| ' MAXVALUE '||greatest(l_seq.max_value,l_seq.last_number)
|| ' NOCACHE'
|| ' ORDER';
EXECUTE IMMEDIATE
'SELECT '||l_seq.sequence_name||'.NEXTVAL FROM DUAL'
INTO l_next;
END IF;
END IF;
-- Prepare Sequence for next use.
IF COALESCE( cycle_flag
, next_value
, increment_by
, min_value
, max_value
, cache_size
, order_flag) IS NOT NULL
THEN
EXECUTE IMMEDIATE
'ALTER SEQUENCE '||l_seq.sequence_name
|| ' INCREMENT BY '||l_seq.increment_by
|| ' MINVALUE '||l_seq.min_value
|| ' MAXVALUE '||l_seq.max_value
|| CASE l_seq.cycle_flag
WHEN 'Y' THEN ' CYCLE' ELSE ' NOCYCLE' END
|| CASE l_seq.cache_size
WHEN 0 THEN ' NOCACHE'
ELSE ' CACHE '||l_seq.cache_size END
|| CASE l_seq.order_flag
WHEN 'Y' THEN ' ORDER' ELSE ' NOORDER' END;
END IF;
END;