Skip to Main Content
  • Questions
  • Performance - Number vs. VARCHAR2 datatypes

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, M.

Asked: January 13, 2017 - 2:25 am UTC

Last updated: January 13, 2017 - 7:23 am UTC

Version: Oracle 12c

Viewed 1000+ times

You Asked

Hello,

I am designing a system that will store and query billions of IP addresses, both IPv4 and IPv6. I was wondering -

(a) Will storing the IP as an integer (e.g Number datatype) instead of a string (e.g. VARCAHR2) give better performance in read or write?
(b) if Number provides better performance, can Number datatype fit a 128 bits IPv6 address?

Thanks,
MN

and Connor said...

a)

Performance-wise I would be very surprised if there is a *significant* difference.

I would be more focussed on what *functional* facilities you require. If you are going to IP range extraction, masking etc etc...then NUMBER might be preferable to get access to the bit-wise operations. Conversely, if you need to store IPv6 representations as they are presented (eg sometimes in the full format, sometimes in the compact format...then perhaps a string is best).

b)

The NUMBER datatype can store IPv6.

Here's an extract from a routine I used to have to convert back and forth between strings and numbers

create or replace
package body XXX is

  --
  -- constants need to be fixed, not expressions if you want to avoid ora-4068
  --
  l_ip41 constant number(12)  := 256;        -- power(256,1);
  l_ip42 constant number(12)  := 65536;      -- power(256,2);
  l_ip43 constant number(12)  := 16777216;   -- power(256,3);
  l_ip44 constant number(12)  := 4294967296; -- power(256,4);
  
  l_ip61 constant number(38)  := 65536;                              --power(65536,1);
  l_ip62 constant number(38)  := 4294967296;                         --power(65536,2);
  l_ip63 constant number(38)  := 281474976710656;                    --power(65536,3);
  l_ip64 constant number(38)  := 18446744073709551616;               --power(65536,4);
  l_ip65 constant number(38)  := 1208925819614629174706176;          --power(65536,5);
  l_ip66 constant number(38)  := 79228162514264337593543950336;      --power(65536,6);
  l_ip67 constant number(38)  := 5192296858534827628530496329220096; --power(65536,7);


function ip_num_from_str(p_ip_str varchar2) return number deterministic is
  l_ip_num     number;
  l_ip1        number;
  l_ip2        number;
  l_dot1       pls_integer;
  l_dot2       pls_integer;
  l_dot3       pls_integer;
  l_dot4       pls_integer;

  l_colon      pls_integer;
  l_colon_cnt  pls_integer;
  l_hex        varchar2(32);
  l_ip_str     varchar2(64);
begin
  if p_ip_str like '%.%' then
    l_dot1   := instr(p_ip_str,'.');
    l_dot2   := instr(p_ip_str,'.',l_dot1+1);
    l_dot3   := instr(p_ip_str,'.',l_dot2+1);
    l_dot4   := instr(p_ip_str,'.',l_dot3+1);
    if l_dot4 > 0 then
       raise_application_error(-20000,'Cannot be resolved to an IP4 address');
    end if;

    l_ip1    := to_number(substr(p_ip_str,1,l_dot1-1));
    l_ip2    := to_number(substr(p_ip_str,l_dot1+1,l_dot2-l_dot1-1));
  
    l_ip_num :=  l_ip43*l_ip1 +  l_ip42*l_ip2  + l_ip41*to_number(substr(p_ip_str,l_dot2+1,l_dot3-l_dot2-1)) + to_number(substr(p_ip_str,l_dot3+1));
    
  elsif p_ip_str like '%:%' then
    l_colon_cnt := length(p_ip_str)-length(replace(p_ip_str,':'));
    if l_colon_cnt != 7 then
       raise_application_error(-20000,'Cannot be resolved to an IP6 address');
    end if;
    
    l_ip_str := p_ip_str||':';
    loop
      l_colon := instr(l_ip_str,':');
      l_hex := l_hex || lpad(substr(l_ip_str,1,l_colon-1),4,'0');
      l_ip_str := substr(l_ip_str,l_colon+1);
      exit when l_ip_str is null ;
    end loop;
    l_ip_num := to_number(l_hex,'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
  end if;
  
  return l_ip_num;
end;


function ip_str_from_num(p_ipnum number) return varchar2 deterministic is
begin
  if p_ipnum < l_ip44 then
    return  mod(trunc(p_ipnum/l_ip43),l_ip41) ||'.'||
            mod(trunc(p_ipnum/l_ip42),l_ip41) ||'.'||
            mod(trunc(p_ipnum/l_ip41),l_ip41) ||'.'||
            mod(p_ipnum,l_ip41);
  else
    return  to_char(mod(trunc(p_ipnum/l_ip67),l_ip61),'fmxxxx') ||':'||
            to_char(mod(trunc(p_ipnum/l_ip66),l_ip61),'fmxxxx') ||':'||
            to_char(mod(trunc(p_ipnum/l_ip65),l_ip61),'fmxxxx') ||':'||
            to_char(mod(trunc(p_ipnum/l_ip64),l_ip61),'fmxxxx') ||':'||
            to_char(mod(trunc(p_ipnum/l_ip63),l_ip61),'fmxxxx') ||':'||
            to_char(mod(trunc(p_ipnum/l_ip62),l_ip61),'fmxxxx') ||':'||
            to_char(mod(trunc(p_ipnum/l_ip61),l_ip61),'fmxxxx') ||':'||
            to_char(mod(p_ipnum,l_ip61),'fmxxxx');
  end if;
end; 




Is this answer out of date? If it is, please let us know via a Comment

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library