OP (Original Poster) упоминает функцию "inet_aton", которая может преобразовывать строковое представление адреса IPv4 в соответствующее ему целочисленное значение. Увы, такой функции нет в Oracle SQL и / или в PL / SQL.
В приведенном ниже ответе я покажу (тривиально!) Способ, которым такая функция может быть написана в PL / SQL. Затем я продемонстрирую, как его можно использовать для решения проблемы ОП.
Примечание ОП упоминает "регулярные выражения" как возможный подход к решению той же проблемы. Я сомневаюсь, что что-нибудь в этом роде будет настолько же эффективным, как то, что я предлагаю ниже. Если блоки являются классными (имеется в виду, если суффикс кратен 8), то действительно, очень просто написать условие regexp_like
(нам нужно сопоставить только первую, две или три "части" из четырех часть IP-адреса). Однако для бесклассовых блоков проблема становится намного более сложной. Конечная заметка
РЕДАКТИРОВАТЬ Для чего я только что попробовал функцию преобразования в таблице с 1,6 миллиона IP-адресов. Он конвертировал их все в числовой формат c за 0,3 секунды. END EDIT
В коде я создаю функцию как отдельный объект (функцию). При желании функция может быть определена в предложении WITH в том же запросе, в котором она используется. Это может привести к немного лучшей производительности. (Можно также использовать формулу из тела функции напрямую в запросе select
, поэтому нигде нет вызовов функций, но прирост производительности, вероятно, будет минимальным.)
Если важна производительность, OP может добавить функциональный индекс в таблицу, в которой хранятся IP-адреса; затем для относительно небольших блоков CIDR запрос должен быть очень быстрым, если фильтр достаточно избирателен.
Вот одна из реализаций функции для преобразования адреса IPv4 из формата из четырех строк в формат целое число:
create or replace function ip_str_to_num (s varchar2) return number
deterministic
as
pragma udf;
begin
return
((to_number(substr(s,1,instr(s,'.',1,1)-1))*256+
to_number(substr(s,instr(s,'.',1,1)+1,instr(s,'.',1,2)-instr(s,'.',1,1)-1))
)*256+
to_number(substr(s,instr(s,'.',1,2)+1,instr(s,'.',1,3)-instr(s,'.',1,2)-1))
)*256+to_number(substr(s,instr(s,'.',1,3)+1));
end;
/
Примечание, в частности pragma udf
- доступно только с Oracle 12.1; это заставляет функцию работать быстрее, если она используется только в контексте SQL.
Далее приведен пример использования этой функции. Я создаю очень маленькую таблицу «доступных адресов». Затем я показываю запрос, где я жестко кодирую блок CIDR (в общем случае его следует заменить на переменную связывания); Я показываю, как мы можем использовать функцию преобразования, чтобы генерировать минимальный и максимальный адреса в блоке в формате цифр c, а затем присоединяюсь к таблице адресов, чтобы найти только те адреса, которые принадлежат блоку.
Данные испытаний:
create table ip_address_list(ip_address varchar2(19));
insert into ip_address_list
select '123.33.2.234' from dual union all
select '230.0.0.1' from dual union all
select '43.233.83.2' from dual union all
select '128.233.2.8' from dual union all
select '72.120.0.1' from dual union all
select '128.232.1.64' from dual
;
commit;
Запрос и результат:
with
inputs(cidr_block) as (
select '128.224.0.0/10' from dual
)
, prep(min_addr, suffix) as (
select ip_str_to_num(substr(cidr_block, 1, instr(cidr_block, '/') - 1)),
substr(cidr_block, instr(cidr_block, '/') + 1)
from inputs
)
, address_range(min_addr, max_addr) as (
select min_addr, min_addr + power(2, 32 - suffix) - 1
from prep
)
select ip_address
from ip_address_list join address_range
on ip_str_to_num(ip_address) between min_addr and max_addr
order by ip_str_to_num(ip_address)
;
IP_ADDRESS
-------------------
128.232.1.64
128.233.2.8