IP 地址存储为十进制 - PL/SQL 显示为虚线四边形
Posted
技术标签:
【中文标题】IP 地址存储为十进制 - PL/SQL 显示为虚线四边形【英文标题】:IP address stored as decimal - PL/SQL to display as dotted quad 【发布时间】:2009-07-05 16:44:44 【问题描述】:我们有一个 Oracle 数据库,其中包含存储为十进制整数的 IP 地址 - 当手动而不是通过 Web 界面操作数据时,这非常痛苦,但是手动操作非常方便,因为网络人员不断要求我们做一些奇怪的事情Web 界面作者没有预料到的事情。
有人可以提供 PL/SQL 或其他方法将这些十进制 IP 显示为点分十进制,即 123.123.123.123 格式吗?
即我希望能够运行如下查询:
select hostname, inttoip(ip_address) from host;
并让inttoip()
过程将 ip_address 显示为 203.30.237.2 而不是 3407801602。
理想情况下,我想要一个也提供反函数的过程,例如
insert into host (hostname,ip_address) values ('some-hostname', iptoint('203.30.237.2'));
我有 perl 可以做到这一点,但我的 PL/SQL/Oracle 知识不足以将其移植到 PL/SQL。
或者一种在 oracle 上下文中将 perl 作为过程语言运行的方法,类似于 postgres 中的以下内容:
CREATE FUNCTION perl_func (integer) RETURNS integer AS $$
<some perl>
$$ LANGUAGE plperl;
会很棒 - 如果可能的话 - 可能会更好,因为我可以用我熟悉的语言在 Oracle 中做很多程序性工作。
【问题讨论】:
Oracle内部有一个JVM。是否存在可以在 JVM 中运行的 perl 版本?如果是这样,您可以使用 perl。例如,可以使用 Oracle JVM 运行 Jython。在这里阅读:forums.oracle.com/forums/… 【参考方案1】:这是你需要的功能:
create or replace
function inttoip(ip_address integer) return varchar2
deterministic
is
begin
return to_char(mod(trunc(ip_address/256/256/256),256))
||'.'||to_char(mod(trunc(ip_address/256/256),256))
||'.'||to_char(mod(trunc(ip_address/256),256))
||'.'||to_char(mod(ip_address,256));
end;
(关于使函数确定性和使用 to_char 的评论 - 谢谢)。
在 Oracle 11G 中,您可以将格式化的 IP 地址设置为主机表上的虚拟列:
alter table host
add formatted_ip_address varchar2(15)
generated always as
( to_char(mod(trunc(ip_address/256/256/256),256))
||'.'||to_char(mod(trunc(ip_address/256/256),256))
||'.'||to_char(mod(trunc(ip_address/256),256))
||'.'||to_char(mod(ip_address,256))
) virtual;
如果需要,可以为查询建立索引。
您的查询变为:
select hostname, formatted_ip_address from host;
【讨论】:
您可以将该函数声明为确定性的。这使得在基于函数的索引 (fbi) 中使用该函数成为可能。使用 fbi 可以解决性能问题。 您甚至可以将 MOD(.....) 定义为基表视图中的列,从而避开 PL/SQL 层。 11g 可以将其作为虚拟列。 我想我自己也会在那里使用显式数字到字符转换。 对不起,我花了 16 年才接受你的回答。 @jason - 肯定只有 12 个? :-)【参考方案2】:CREATE OR REPLACE
FUNCTION inttoip(ip_address IN INTEGER) RETURN VARCHAR2 IS
v8 VARCHAR2(8);
BEGIN
-- 1. convert the integer into hexadecimal representation
v8 := TO_CHAR(ip_address, 'FMXXXXXXXX');
-- 2. convert each XX portion back into decimal
RETURN to_number(substr(v8,1,2),'XX')
|| '.' || to_number(substr(v8,3,2),'XX')
|| '.' || to_number(substr(v8,5,2),'XX')
|| '.' || to_number(substr(v8,7,2),'XX');
END inttoip;
CREATE OR REPLACE
FUNCTION iptoint(ip_string IN VARCHAR2) RETURN INTEGER IS
d1 INTEGER;
d2 INTEGER;
d3 INTEGER;
q1 VARCHAR2(3);
q2 VARCHAR2(3);
q3 VARCHAR2(3);
q4 VARCHAR2(3);
v8 VARCHAR2(8);
BEGIN
-- 1. parse the input, e.g. '203.30.237.2'
d1 := INSTR(ip_string,'.'); -- first dot
d2 := INSTR(ip_string,'.',1,2); -- second dot
d3 := INSTR(ip_string,'.',1,3); -- third dot
q1 := SUBSTR(ip_string, 1, d1 - 1); -- e.g. '203'
q2 := SUBSTR(ip_string, d1 + 1, d2 - d1 - 1); -- e.g. '30'
q3 := SUBSTR(ip_string, d2 + 1, d3 - d2 - 1); -- e.g. '237'
q4 := SUBSTR(ip_string, d3 + 1); -- e.g. '2'
-- 2. convert to a hexadecimal string
v8 := LPAD(TO_CHAR(TO_NUMBER(q1),'FMXX'),2,'0')
|| LPAD(TO_CHAR(TO_NUMBER(q2),'FMXX'),2,'0')
|| LPAD(TO_CHAR(TO_NUMBER(q3),'FMXX'),2,'0')
|| LPAD(TO_CHAR(TO_NUMBER(q4),'FMXX'),2,'0');
-- 3. convert to a decimal number
RETURN TO_NUMBER(v8, 'FMXXXXXXXX');
END iptoint;
【讨论】:
【参考方案3】: -- INET ATON en INET NTOA and helper function GET TOKEN
CREATE OR REPLACE function inet_ntoa (ip integer) return varchar2
is
ip1 integer;
ip2 integer;
ip3 integer;
ip4 integer;
ipi integer := ip;
begin
ip1 := floor(ipi/power(2,24));
ipi := ipi - (ip1*power(2,24));
ip2 := floor(ipi/power(2,16));
ipi := ipi - (ip2*power(2,16));
ip3 := floor(ipi/power(2,8));
ipi := ipi - (ip3*power(2,8));
ip4 := ipi;
return ip1||'.'||ip2||'.'||ip3||'.'||ip4;
end;
/
CREATE OR REPLACE FUNCTION get_token (the_list VARCHAR2,the_index NUMBER, delim VARCHAR2 := '.') RETURN VARCHAR2
IS
start_pos INTEGER;
end_pos INTEGER;
BEGIN
IF the_index = 1 THEN
start_pos := 1;
ELSE
start_pos := INSTR (the_list, delim, 1, the_index - 1);
IF start_pos = 0 THEN
RETURN NULL;
ELSE
start_pos := start_pos + LENGTH (delim);
END IF;
END IF;
end_pos := INSTR (the_list, delim, start_pos, 1);
IF end_pos = 0 THEN
RETURN SUBSTR (the_list, start_pos);
ELSE
RETURN SUBSTR (the_list, start_pos, end_pos - start_pos);
END IF;
END get_token;
/
CREATE OR REPLACE function inet_aton (ip varchar2) return integer
is
invalid_ip_adres exception;
pragma exception_init(invalid_ip_adres,-6502);
ipi integer;
begin
ipi := get_token(ip,4)
+(get_token(ip,3)*power(2,8))
+(get_token(ip,2)*power(2,16))
+(get_token(ip,1)*power(2,24));
return ipi;
exception
when invalid_ip_adres
then
return null;
end;
/
【讨论】:
我在寻找inet_aton(),在这里我找到了这篇文章,以及你的函数。非常感谢!附言你应该声明 inet_* 确定性:) 欢迎您。谢谢你的提示。仅包含计算的函数应声明为确定性的。 我不确定您所说的仅包含计算是什么意思。 “确定性”告诉 Oracle,函数结果永远不会在相同的输入上改变。所以,如果我想在任何 inet_* 函数上添加函数索引,它们必须这样声明(并且它们本质上已经是确定性的)。以上是关于IP 地址存储为十进制 - PL/SQL 显示为虚线四边形的主要内容,如果未能解决你的问题,请参考以下文章