Why do these 2 oracle functions perform differently? - oracle

I wrote a function in Oracle to convert IP addresses to integers. It seemed slow. I wrote a second function to do the same thing only faster. Unfortunately it ended up slower and I do not know why.
Original function;
FUNCTION GET_IP_INTEGER
(
IP_IN IN VARCHAR2
) RETURN NUMBER AS
DOT_COUNTER INTEGER;
CURRENT_DOT INTEGER;
LAST_DOT INTEGER := 1;
CURRENT_INTEGER INTEGER := 0;
OUTPUT_INTEGER INTEGER := 0;
BEGIN
FOR DOT_COUNTER IN 1..3
LOOP
CURRENT_DOT := INSTR(IP_IN,'.',LAST_DOT);
CURRENT_INTEGER := TO_NUMBER(SUBSTR(IP_IN,LAST_DOT,CURRENT_DOT - LAST_DOT));
LAST_DOT := CURRENT_DOT + 1;
CASE DOT_COUNTER
WHEN 1 THEN CURRENT_INTEGER := CURRENT_INTEGER * 16777216;
WHEN 2 THEN CURRENT_INTEGER := CURRENT_INTEGER * 65536;
WHEN 3 THEN CURRENT_INTEGER := CURRENT_INTEGER * 256;
END CASE;
OUTPUT_INTEGER := OUTPUT_INTEGER + CURRENT_INTEGER;
CURRENT_INTEGER := 0;
END LOOP;
CURRENT_INTEGER := TO_NUMBER(SUBSTR(IP_IN,LAST_DOT));
OUTPUT_INTEGER := OUTPUT_INTEGER + CURRENT_INTEGER;
RETURN OUTPUT_INTEGER;
END GET_IP_INTEGER;
It picks everything apart and works well. But I thought I could do better so I wrote this;
FUNCTION GET_IP_INTEGER1
(
IP_IN IN VARCHAR2
) RETURN NUMBER AS
OCTET_COUNTER INTEGER;
CURRENT_INTEGER INTEGER := 0;
OUTPUT_INTEGER INTEGER := 0;
BEGIN
FOR OCTET_COUNTER IN 1..4
LOOP
CURRENT_INTEGER := TO_NUMBER(REGEXP_SUBSTR(IP_IN,'\w+',1,OCTET_COUNTER));
CURRENT_INTEGER := POWER(2,24 - ((OCTET_COUNTER-1)*8)) * CURRENT_INTEGER;
OUTPUT_INTEGER := OUTPUT_INTEGER + CURRENT_INTEGER;
END LOOP;
RETURN OUTPUT_INTEGER;
END GET_IP_INTEGER1;
This also works but seems to run much (about twice as long) slower. I would assume that either the power function or the regexp_substr is a pig. But I was hoping someone with more knowledge might point out which, and/or why.

Here is my little piece of knowledge: in oracle 11g, you have the hierarchical PL/SQL profiler. This will show you where your pl/sql is spending its time.

regexp_substr will be slower than a regular substr, and there will be a bit of overhead with the power. The most time would in in the regexp_substr though.
im curious why you say they are "slow" though. i assume you mean over a LOT of calls? as the routines are pretty snappy when i tested them.
Also, this type of function would benefit greatly from native compilation (which is easy to do in 11g, as the dba doesn't have to do anything to get this working).
finally you may find this to be marginally quicker (esp with native compilation).
create or replace function get_ip_integer3(ip_in in varchar2)
return integer
as
result integer := 0;
begin
result := result + 16777216 * substr(ip_in, 1, instr(ip_in, '.') - 1);
result := result +
65536 * substr(ip_in, instr(ip_in, '.') + 1,
instr(ip_in, '.', 1, 2) - instr(ip_in, '.') - 1);
result := result +
256 * substr(ip_in, instr(ip_in, '.', 1, 2) + 1,
instr(ip_in, '.', 1, 3) - instr(ip_in, '.', 1, 2) - 1);
result := result + substr(ip_in, instr(ip_in, '.', 1, 3) + 1);
return result;
end get_ip_integer3;

Related

Convert Binary's 2's compliment to decimal in oracle SQL

I have a sample number in a column of oracle table which is binary's 2's complimanet -
e.g 0110001000110111
I want to convert this to normal decimal number in 2's compliment.
Expected output-
Reference link - https://www.rapidtables.com/convert/number/decimal-to-binary.html
You can loop as applying powers of 2 while multiplying by each bit(0 or 1) starting from the right end of your presented value such as
SET SERVEROUTPUT ON
DECLARE
bin_nr VARCHAR2(100) := '0110001000110';
dec_nr NUMBER;
BEGIN
FOR i IN 1..LENGTH(bin_nr)
LOOP
dec_nr := NVL(dec_nr,0) + SUBSTR(bin_nr,-i,1)*(2**(i-1));
END LOOP;
DBMS_OUTPUT.PUT_LINE(dec_nr);
END;
/
which results 3142 as the decimal value.
Demo
For 2s compliment, the most-significant bit represents the sign bit and if that is 1 then you have a negative number:
DECLARE
bin_nr VARCHAR2(100) := '1111001110111010';
sign PLS_INTEGER;
dec_nr PLS_INTEGER;
BEGIN
IF SUBSTR(bin_nr, 1, 1) = '1' THEN
bin_nr := TRANSLATE(bin_nr, '01', '10');
sign := -1;
dec_nr := 1;
ELSE
sign := 1;
dec_nr := 0;
END IF;
FOR i IN 1 .. LENGTH(bin_nr) LOOP
IF SUBSTR(bin_nr, -i, 1) = '1' THEN
dec_nr := dec_nr + POWER(2, i-1);
END IF;
END LOOP;
dec_nr := dec_nr * sign;
DBMS_OUTPUT.PUT_LINE(dec_nr);
END;
/
Outputs -3142
If you are expecting an N-bit binary number as the input (for example, the link in the question expects a 16-bit binary number as an input for 2s compliment) then you should LPAD with zeroes if you have fewer than that many bits.
db<>fiddle here

PL SQL - Lazy Caterer's Sequence

I need to write a PL/SQL program to print out the first 10 numbers of Lazy Caterer's sequence. I'm having trouble. I don't understand how to do this
Lazy caterer's sequence has the formula F(1)=2, F(n)=F(n-1)+n.
E.g., F(2) = F(1) + 2 = 2+2=4; F(3) = F(2)+3 = 4+3=7
Please use a loop. The numbers you print out should look like
2
4
7
...
56
DECLARE
n NUMBER := 1;
BEGIN
FOR i IN 1..10 LOOP
n := n + i;
DBMS_OUTPUT.PUT_LINE(n);
END LOOP;
END;
/
I cheated and looked up Lazy Caterer's algorithm on Wikipedia. This is an implementation of the simplistic version of the formula:
create or replace function lc_algo
(n in number)
return number as
begin
return (power(n,2) + n + 2) / 2;
end;
/
I admit it doesn't use a loop, but it's hard to see why anybody would need to. However, if your assignment insists on loops (because performance is not your bag) here you go:
create or replace function lc_algo
(n in number)
return number
as
rv simple_integer := 1;
begin
for i in 1..n loop
rv := rv + i;
end loop;
return rv;
end;
/
To get the first ten numbers (with whatever version):
select lc_algo(level) -- assuming you don't want to start with f(0)
from dual
connect by level <= 10
/

Find block number and floor by flat number

Imagine - there's a house with 80 flats. It has 4 floors and 5 blocks. Each block has 4 flats.
User is asked to input flat number and Pascal program is supposed to calculate and output flat number. This must be calculated using some kind of formula. The only tip I have is that I have to use div and mod operations.
This is how the house looks like -
So far, I've created program, that loops through all 80 flats and after each 16 flats increases block value and after each 4 blocks increases stair.
This is my code:
program project1;
var
i, floors, blocks, flats, flat, block, floor, blockCounter, floorCounter : integer;
begin
floors := 4;
blocks := 5;
flats := 80;
while true do
begin
write('Flat number: ');
read(flat);
block := 1;
floor := 1;
blockCounter := 0;
floorCounter := 0;
for i := 1 to 80 do
begin
blockCounter := blockCounter + 1;
floorCounter := floorCounter + 1;
if (floorCounter = 4) then
begin
floorCounter := 0;
floor := floor + 1;
end;
if (blockCounter > 16) then
begin
block := block + 1;
blockCounter := 0;
floorCounter := 0;
floor := 1;
end;
if (i = flat) then
begin
writeln('Flat nr. ', flat, ' is in ', floor, '. floor and in ', block, '. block!');
end;
end;
end;
end.
Is there anyone who can help me with this?
I've finally solved my problem myself.
I finally undersood how div works, so I was able to solve this.
program Maja;
var dzivoklis, kapnutelpa, stavs : integer;
begin
while true do
begin
write('Ievadi dzivokla numuru: ');
read(dzivoklis);
kapnutelpa := ((dzivoklis - 1) div 16) + 1;
stavs := (((dzivoklis - 1) mod 16) div 4) + 1;
writeln('Kapnutelpa: ', kapnutelpa);
writeln('Stavs: ', stavs);
writeln();
end;
end.

Invalid Floating point operation Pascal

I'm coding a program with Pascal in fastreport, The problem appear when I try to show the result of a division. this is my program:
var
relec_tp_for_pa, tp_for_pa, relec_tt_tp, total_tp, coef_relec : Real = 0;
relec_vt : Integer = 0;
procedure MasterData1OnBeforePrint(Sender: TfrxComponent);
begin
if ( ( <EJobsQuery."SERV_ID"> = 54 ) ) then
begin
relec_tp_for_pa := tp_for_pa;
relec_vt := relec_vt + <EJobsQuery."EJOB_VOLUME">;
relec_tt_tp := relec_tt_tp + tp;
total_tp := total_tp + tp;
end;
coef_relec := StrToFloat(FormatFloat('0.0000', relec_tt_tp / total_tp));
relec_pa.Text := FormatFloat('0.00', relec_vt /(relec_tp_for_pa * coef_relec));
relec_pr.Text := FloatToStr( relec_vt / relec_tt_tp );
end;
Both of this divisions:
-relec_vt /(relec_tp_for_pa * coef_relec)
-relec_vt / relec_tt_tp
are giving me the error : Invalid Floating point operation
I'm coding in the FastReport 4.11.4 a report.
under 32 bits OS if this can help.
Thanks again.
From what you've coded it seems that You divide by zero!
-relec_vt /(relec_tp_for_pa * coef_relec)
-relec_vt / relec_tt_tp
Since
relec_tp_for_pa := tp_for_pa
is definetly ZERO!
relec_tp_for_pa * coef_relec
Will also be zero!
For relec_tt_tp I just can try to guess, since this code is not a complete example.
total_tp := total_tp + tp
total_tp is zero, and if tp is also zero You'd end up in a division by zero.
Generally I'd always check if your divisor is not zero!

All sums of a number

I need an algorithm to print all possible sums of a number (partitions).
For example: for 5 I want to print:
1+1+1+1+1
1+1+1+2
1+1+3
1+2+2
1+4
2+3
5
I am writing my code in Pascal. So far I have this:
Program Partition;
Var
pole :Array [0..100] of integer;
n :integer;
{functions and procedures}
function Minimum(a, b :integer): integer;
Begin
if (a > b) then Minimum := b
else Minimum := a;
End;
procedure Rozloz(cislo, i :integer);
Var
j, soucet :integer;
Begin
soucet := 0;
if (cislo = 0) then
begin
for j := i - 1 downto 1 do
begin
soucet := soucet + pole[j];
if (soucet <> n) then
Write(pole[j], '+')
else Write(pole[j]);
end;
soucet := 0;
Writeln()
end
else
begin
for j := 1 to Minimum(cislo, pole[i - 1]) do
begin
pole[i] := j;
Rozloz(cislo - j, i + 1);
end;
end;
End;
{functions and procedures}
{Main program}
Begin
Read(n);
pole[0] := 101;
Rozloz(n, 1);
Readln;
End.
It works good but instead of output I want I get this:
1+1+1+1+1
2+1+1+1
2+2+1
3+1+1
3+2
4+1
5
I can't figure out how to print it in right way. Thank you for help
EDIT: changing for j:=i-1 downto 1 to for j:=1 to i-1 solves one problem. But my output is still this: (1+1+1+1+1) (2+1+1+1) (2+2+1) (3+1+1) (3+2) (4+1) (5) but it should be: (1+1+1+1+1) (1+1+1+2) (1+1+3) (1+2+2) (1+4) (2+3) (5) Main problem is with the 5th and the 6th element. They should be in the opposite order.
I won't attempt Pascal, but here is pseudocode for a solution that prints things in the order that you want.
procedure print_partition(partition);
print "("
print partition.join("+")
print ") "
procedure finish_and_print_all_partitions(partition, i, n):
for j in (i..(n/2)):
partition.append(j)
finish_and_print_all_partitions(partition, j, n-j)
partition.pop()
partition.append(n)
print_partition(partition)
partition.pop()
procedure print_all_partitions(n):
finish_and_print_all_partitions([], 1, n)

Resources