How can I improve Oracle performance when performing a user defined Statistics function? - oracle

An associate of mine converted a javascript inverse chi-square routine to Oracle. The good news is that it returns the same results as the javascript routine. The bad news is that where it takes 1.5 seconds to return the result in IE or Chrome, it takes 23 seconds in Oracle. Of that 23 seconds >99% is CPU time.
There are two loops in the routines, an outer loop that executes 36 times for the values we are testing with, and an inner loop that runs 10,753 times for the each iteration of the outer loop. It does the same loop in both JS as it does in Oracle. For each iteration of the inner loop it executes an EXP function and an LN function, both of which are intrinsic in both languages.
I have compiled the Oracle code both Interpreted as well as Native, with little if any change (.045 second difference).
I have three questions;
Why is Oracle so slow/how can I improve it?
Is there an intrinsic inverse chi square function in Oracle.
Does anyone have an inverse chi-square function that does not require the iterative looping (or not as much) as the one I am using?
A bonus question is;
Does anyone have a routine that computes Confidence Intervals in PL/SQL, or a language that could easily be converted to PL/SQL?
As requested, here is the code, which is a bit long (The main routine is CRITCHI for testing P=0.975 and DF=21507.38);
BIGX Number :=20;
FUNCTION POZ(Z IN NUMBER) RETURN NUMBER IS
Y NUMBER;
X NUMBER;
W NUMBER;
Z_MAX NUMBER;
XXX NUMBER;
BEGIN
Z_MAX:=6.0;
IF (Z=0) THEN
X:= 0.0;
ELSE
Y := 0.5 * ABS(Z);
IF (Y >= (Z_MAX * 0.5)) THEN
X:= 1.0;
ELSIF (y < 1.0) THEN
W:= Y * Y;
X:= ((((((((0.000124818987 * W
- 0.001075204047) * W + 0.005198775019) * W
- 0.019198292004) * W + 0.059054035642) * W
- 0.151968751364) * W + 0.319152932694) * W
- 0.531923007300) * W + 0.797884560593) * Y * 2.0;
ELSE
Y:= Y-2.0;
Y:= (((((((((((((-0.000045255659 * Y
+ 0.000152529290) * Y - 0.000019538132) * Y
- 0.000676904986) * Y + 0.001390604284) * Y
- 0.000794620820) * Y - 0.002034254874) * Y
+ 0.006549791214) * Y - 0.010557625006) * Y
+ 0.011630447319) * Y - 0.009279453341) * Y
+ 0.005353579108) * Y - 0.002141268741) * Y
+ 0.000535310849) * Y + 0.999936657524;
END IF;
END IF;
IF (Z>0.0) THEN
XXX:=((X + 1.0) * 0.5);
ELSE
XXX:= ((1.0 - x) * 0.5);
END IF;
RETURN XXX;
END POZ;
FUNCTION EX(X IN NUMBER) RETURN NUMBER IS
BEGIN
IF (x < -BIGX) THEN
RETURN 0;
ELSE
RETURN EXP(X);
END IF;
END EX;
FUNCTION POCHISQ(X IN NUMBER, DF IN NUMBER) RETURN NUMBER IS
A NUMBER;
Y NUMBER;
S NUMBER;
E NUMBER;
C NUMBER;
Z NUMBER;
X1 NUMBER;
EVEN BOOLEAN; /* True if df is an even number */
LOG_SQRT_PI NUMBER := 0.5723649429247000870717135; /* log(sqrt(pi)) */
I_SQRT_PI NUMBER := 0.5641895835477562869480795; /* 1 / sqrt(pi) */
b1 PLS_INTEGER;
b2 PLS_INTEGER;
e1 PLS_INTEGER;
e2 PLS_INTEGER;
BEGIN
b1 := DBMS_UTILITY.GET_TIME();
b2 := DBMS_UTILITY.GET_CPU_TIME();
X1:=X;
IF (X1 <= 0.0 OR DF < 1) THEN
RETURN 1.0;
END IF;
A:= 0.5 * X1;
EVEN:= (MOD(DF,2)=0);
IF (DF > 1) THEN
Y := ex(-A);
END IF;
IF EVEN THEN
S:=Y;
ELSE
S:=(2.0 * poz(-sqrt(X1)));
END IF;
IF (DF > 2) THEN
X1:= 0.5*(DF-1.0);
IF EVEN THEN
Z:=1.0;
ELSE
Z:=0.5;
END IF;
IF (A > BIGX) THEN
IF EVEN THEN
E:=0.0;
ELSE
E:=LOG_SQRT_PI;
END IF;
C:= LN(A);
/* Timming snippet */
e1 := DBMS_UTILITY.GET_TIME() - b1;
e2 := DBMS_UTILITY.GET_CPU_TIME() - b2;
--DBMS_OUTPUT.PUT_LINE( '0-GET_TIME elapsed = ' || e1 || ' hsecs.' );
--DBMS_OUTPUT.PUT_LINE( '0-GET_CPU_TIME elapsed = ' || e2 || ' hsecs.' );
/* End of Timming snippet */
WHILE (Z <= X1)
LOOP
E:= LN(Z) + E;
S:=S+EX(C * Z - A - E);
Z:=Z+1.0;
END LOOP;
e1 := DBMS_UTILITY.GET_TIME() - b1;
e2 := DBMS_UTILITY.GET_CPU_TIME() - b2;
--DBMS_OUTPUT.PUT_LINE( '1-GET_TIME elapsed = ' || e1 || ' hsecs. Z= ' || Z );
--DBMS_OUTPUT.PUT_LINE( '1-GET_CPU_TIME elapsed = ' || e2 || ' hsecs.' );
RETURN S;
ELSE
IF EVEN THEN
E:=1.0;
ELSE
E:=(I_SQRT_PI / sqrt(A));
END IF;
C:= 0.0;
WHILE (Z <= X1)
LOOP
E:= E * (A / Z);
C:= C + E;
Z:=Z+ 1.0;
END LOOP;
e1 := DBMS_UTILITY.GET_TIME() - b1;
e2 := DBMS_UTILITY.GET_CPU_TIME() - b2;
--DBMS_OUTPUT.PUT_LINE( '2-GET_TIME elapsed = ' || e1 || ' hsecs.' );
--DBMS_OUTPUT.PUT_LINE( '2-GET_CPU_TIME elapsed = ' || e2 || ' hsecs.' );
RETURN C * Y + S;
END IF;
ELSE
e1 := DBMS_UTILITY.GET_TIME() - b1;
e2 := DBMS_UTILITY.GET_CPU_TIME() - b2;
--DBMS_OUTPUT.PUT_LINE( '3-GET_TIME elapsed = ' || e1 || ' hsecs.' );
--DBMS_OUTPUT.PUT_LINE( '3-GET_CPU_TIME elapsed = ' || e2 || ' hsecs.' );
RETURN S;
END IF;
END POCHISQ;
/* CRITCHI -- Compute critical chi-square value to
produce given p. We just do a bisection
search for a value within CHI_EPSILON,
relying on the monotonicity of pochisq(). */
FUNCTION CRITCHI(P IN NUMBER, DF IN NUMBER) RETURN NUMBER IS
CHI_EPSILON NUMBER:= 0.000001; /* Accuracy of critchi approximation */
CHI_MAX NUMBER:= 99999.0; /* Maximum chi-square value */
minchisq NUMBER:= 0.0;
maxchisq NUMBER:= CHI_MAX;
chisqval NUMBER;
dummy_count number := 0;
BEGIN
IF (p <= 0.0) THEN
RETURN maxchisq;
ELSE
IF (p >= 1.0) THEN
RETURN 0.0;
END IF;
END IF;
chisqval:= df / sqrt(p); /* fair first value */
WHILE ((maxchisq - minchisq) > CHI_EPSILON)
LOOP
if (pochisq(chisqval, df) < p) THEN
maxchisq:= chisqval;
ELSE
minchisq:= chisqval;
END IF;
chisqval:= (maxchisq + minchisq) * 0.5;
dummy_count := dummy_count + 1;
END LOOP;
--DBMS_OUTPUT.PUT_LINE('chisqval = ' || chisqval);
RETURN chisqval;
END CRITCHI;

For the benefit of future seekers, who might not have the patience to trawl through all the comments, the following optimizations were applied to the program to make it run fast.
All the numeric variables were declared as BINARY_INTEGER(#). Find out more.
Functions were declared as deterministic.
Functions were compiled into native C.
(#) On more modern versions of the database PLS_INTEGER is preferred (simply because BINARY_INTEGER is old and deprecated - it gets converted to PLS_INTEGER under the covers) .
NB - if the OP or #AlexPoole would care to write a similar response, I'll happily upvote their answer and delete this one.

Related

Forms in Delphi 7

There are 2 codes in Delphi 7. I need to make forms for them so that all variables are written to the form by the crawler himself, and not by the programmer through the code.
I tried to create a form from scratch. I tried to build it on a ready-made code. Unfortunately, my knowledge of programming in Delphi 7 is too small to understand the documentation for forms written in a rather complex language.
FIRST:
program p1;
{$APPTYPE CONSOLE}
uses
SysUtils, Math, Windows;
var
x, z: integer;
var
RES,s, sp, p, ps, y: real;
begin
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
x := 1;
y := 2.25;
z := 3;
ps := 1;
for x := 1 to 7 do
begin
sp := 0;
for z := 3 to 10 do
begin
s := Arctan(y / z + x / y) / power(abs(y - x - z), 1 / 3);
sp := sp + s;
//Writeln('Сумма ', sp:0:3);
end;
p := (power(2.3, 4 / x) * abs(y - x)) / (sqrt(sqr(x) + sqr(y) + 1.5)) + sp;
ps := ps * p;
//Writeln('Произведение ', ps:0:3);
end;
RES := ps;
Writeln(RES:0:3);
Readln;
end.
SECOND:
program p2;
{$APPTYPE CONSOLE}
uses
SysUtils, Math, Windows;
const
H = 0.4;
Xmin = -3;
Xmax = 2.9;
var
x, y, W: real;
begin
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
x := Xmin;
while (x < Xmax) do
begin
if (x > 0.1) and (x < 2) then begin
W := power(x, 1 / 3) + ln(x);
y := ((ln(sqr(W) + W + 1)) * cos(4 * x)) / (exp(-2) + 2);
end
else if (x <= 0.1) then begin
W := sqr(sin(x)) + 4 * x;
y := ((ln(sqr(W) + W + 1)) * cos(4 * x)) / (exp(-2) + 2);
end
else begin
W := 2.6 * sqr(x) - 3.7;
y := ((ln(sqr(W) + W + 1)) * cos(4 * x)) / (exp(-2) + 2);
end;
x := x + H;
Write('X= ', x:0:3);
Write(' ');
Writeln('Y= ', y:0:3);
end;
Readln;
end.
File > New > Forms Application
That will create a new GUI (not Console) project with a blank Form. Design the Form with UI controls and event handlers as needed, and then copy/paste your code above into the generated code as needed.

The '+' operation is not applicable to the types function(x: real): real and real. Check the operation of the program for a = 0.1; b = 1.0; h = 0.1;

Check the operation of the program for a = 0.1; b = 1.0; h = 0.1; select the value of parameter n depending on the task.
Why am I getting an error? What is the best way to solve this problem? How to simplify?
var i, n: integer;
x, k, h, sx: real;
function Y(x: real): real;
begin
Y := x * arctan(x) - 0.5 * ln(1.0 + x * x)
end;
function S(x: real): real;
var sum, xx, p, znak, e: real;
begin
S := 0.5 * x * x;
p := x * x;
xx := - x * x;
k := 2;
e := 1e303;
while abs(e) > 1e-14 do
begin
k := k + 2;
p := p * xx;
e := p / (k * (k - 1));
S := S + e
end
end;
begin
h := 0.1;
writeln('x': 2, 'S(x)': 14,
'Y(x)': 18, 'n': 15);
for i := 1 to 10 do
begin
x := i * h;
sx := S(x);
n := round(k / 2);
writeln(x: 3: 1, sx: 18: 14,
Y(x): 18: 14, n: 10)
end
end.
-->The '+' operation is not applicable to the types function(x: real): real and real
I tried to solve the problem based on the fact that x is the range a to b with a step h:
program test;
var y, a, b, h, x, Sx, Yx, n:real;
begin
a:=0.1;
b:=1.0;
h:=0.1;
x:=a;
n:=0;
while x<=b do
begin
Yx:= x*arctan(x)-ln(sqrt(1+exp(x)));
x:=x+h;
writeln(Yx);
writeln('---------------------', n); n:=n+1;
end;
end.
But I do not know how to get S(x)
The error message means that the first argument of + is a function. I'll bet this is the S := S + e line. While you can assign to S to set the return value of S, you can't read it back like that.
You can refer to a function inside that function; this is used with recursion. But then you'll need to actually call yourself. E.g. Fibonacci := Fibonacci(i-1) * i. Now the left side of * is not a function, but the result of a function call.
Solution: just use a temporary variable, and assign that to S at the very end; of S

“END” expected but “IF” found. Stalked with some problem in turbo pascal code

program calc;
var a,b,c,d:real;
Begin
write('a=');readln(a);
write('b=');readln(b);
write('c=');readln(c);
if a = 0 then
if b = 0 then
if c = 0 then
writeln('equation undetermined,S=R')
else
begin
d := b * b - 4 * a * c; <<<< missed ';'?
if (d >= 0) then
begin
writeln('x1=',(-b-sqrt(d))/(2* a):6:2 ); <<< missed ')' ?
writeln('x2=',(-b+sqrt(d))/(2* a):6:2 ); <<< missed ')' ?
end;
else
writeln ('Equation has no real solutions');
end;
readln;
End.
I think you want to do this:
Program Calc;
var a,b,c,d: Real;
Begin
Write('a='); ReadLn(a);
Write('b='); ReadLn(b);
Write('c='); ReadLn(c);
if (a = 0) or (b = 0) or (c = 0) then
WriteLn('equation undetermined,S=R')
else
Begin
d := b * b - 4 * a * c;
if (d >= 0) then
Begin
WriteLn('x1=', (-b - sqrt(d)) / (2 * a):6:2 );
WriteLn('x2=', (-b + sqrt(d)) / (2 * a):6:2 );
end;
else
WriteLn('Equation has no real solutions');
end;
ReadLn;
End.
if ...
then if ...
then ...
else ...
might also compile as
if ...
then if ...
then ...
else ...
instead use
if ...
then begin
if ...
then ...
end
else ...

PL/SQL Check Digit, luhn using MOD 11

So here is the question:
Write code to take in an id and determine if the check digit is correct
UPDATED CODE:
Set SERVEROUTPUT ON
DECLARE
val_num NUMBER := '&user_input';
holder NUMBER := 0;
y NUMBER := 0;
conv_string VARCHAR2(20);
BEGIN
conv_string := to_char(val_num*10);
for x in 1..length(conv_string) loop
y := to_number(substr(conv_string, -x, 1));
if mod(x,2) = 0 then
y := y * 2;
if y > 9 then
y := y - 9;
end if;
end if;
holder := holder + y;
end loop;
dbms_output.put_line ('Check is '||(11-Mod(holder, 11)));
END luhn;
/
SET SERVEROUTPUT ON
The return is:
SQL> # loop
Enter value for user_input: 036532
old 2: val_num NUMBER := '&user_input';
new 2: val_num NUMBER := '036532';
Check is 2
It should be 6
Before actual execution
SET SERVEROUTPUT ON
to enable SQL*Plus to fetch database output buffer.
Here is solution: https://community.oracle.com/thread/837639?start=0&tstart=0
There are lots of different variations of the luhn algorithm, so looking at these implementations and your (I think incomplete) description in the comments I think this may be fairly close to what you are looking for, and gives the correct checksum for 036532 as per your initial question.
Hope it is helpfull
Set SERVEROUTPUT ON
DECLARE
val_num number := '036532';
holder NUMBER := 0;
y NUMBER := 0;
conv_string VARCHAR2(20);
BEGIN
conv_string := to_char(val_num);
FOR X IN 1..LENGTH(CONV_STRING) LOOP
Y := TO_NUMBER(SUBSTR(CONV_STRING, -X, 1));
IF ((X+1) > 10) THEN
Y := Y * 10;
ELSE
Y := Y * (X + 1);
END IF;
IF (Y >= 10) THEN
HOLDER := HOLDER + TO_NUMBER(substr(TO_CHAR(Y), 1, 1)) + TO_NUMBER(substr(TO_CHAR(Y), 2, 1));
ELSE
HOLDER := HOLDER + Y;
END IF;
END LOOP;
HOLDER := MOD(HOLDER, 11);
Holder := 11 - mod(holder, 11);
dbms_output.put_line ('Check is '|| holder);
END luhn;
/
SET SERVEROUTPUT ON

ORACLE PL/SQL: Table or view does not exist

I'd appreciate some help on this topic since I am making my first steps into PL/SQL:
When trying to create a function, I get an error: "Table or view does not exist" for all 5 Select statements (and for the statement in the cursor declaration as well). I am sure all tables exist.
Many thanks in advance.
CREATE OR REPLACE FUNCTION ZINSKALK (vtid IN NUMBER, intart IN INTEGER)
RETURN NUMBER
IS
j INTEGER := 0;
p NUMBER := 1;
q NUMBER := 0;
t NUMBER := 1;
r NUMBER := 0;
lz INTEGER := 0;
modusvn INTEGER := 0;
w1 NUMBER := 0;
w2 NUMBER := 0;
i INTEGER := 0;
TYPE ARRAY_TYP IS VARRAY (200) OF NUMBER (10, 2);
RARRAY ARRAY_TYP;
CURSOR C1
IS
SELECT vtobslpos.anzahl, vtobslpos.betrag
FROM vtobsl, vtobslpos
WHERE vtobsl.sysid = vtobslpos.sysvtobsl
AND vtobsl.sysid IN
(SELECT MAX (vtobsl.sysid)
FROM vtobsl
WHERE vtobsl.sysvt = vtid AND vtobsl.rang = 100)
ORDER BY vtobslpos.rang ASC;
BEGIN
SELECT SUM (vtobslpos.anzahl)
INTO lz
FROM vtobslpos, vtobsl
WHERE vtobslpos.sysvtobsl = vtobsl.sysid
AND vtobsl.sysvt = vtid
AND vtobsl.sysid IN (SELECT MAX (sysid)
FROM vtobsl
WHERE sysvt = vtid AND rang = 100);
SELECT vtobsl.modus
INTO modusvn
FROM vtobsl
WHERE vtobsl.sysid IN (SELECT MAX (sysid)
FROM vtobsl
WHERE sysvt = vtid AND rang = 100);
SELECT bgextern
- sz
+ (CASE intart
WHEN 1 THEN 0
WHEN 2 THEN - (sz2 + disagio)
WHEN 3 THEN (sz2 + disagio - provision)
ELSE 0
END)
INTO W1
FROM vt
WHERE sysid = vtid;
SELECT rw + rsv
INTO w2
FROM vt
WHERE sysid = vtid;
i := (CASE WHEN modusvn = 0 THEN 1 ELSE 0 END);
RARRAY (1) := -w1;
RARRAY (lz + 1) := w2;
FOR x IN C1
LOOP
FOR y IN 1 .. x.anzahl
LOOP
i := i + 1;
RARRAY (i) := RARRAY (i) + x.betrag;
END LOOP;
END LOOP;
WHILE ABS (p) >= 0.001
LOOP
p := 0;
q := 0;
j := 0;
FOR z IN 1 .. lz + 1
LOOP
j := j + 1;
p := p + RARRAY (j) * POWER (t, j - 1);
q := q + (j - 1) * RARRAY (j) * POWER (t, j - 2);
END LOOP;
t := t - p / q;
END LOOP;
r := 1200 / t - 1200;
RETURN r;
EXCEPTION
WHEN OTHERS
THEN
RETURN 0;
END;
I assume the table belong to another schema than the schema into which your function is compiled. If you have granted select on these table via role rather than directly, Oracle will give you an ORA-00942.
See this stackoverflow question for more information.
Did you compile the procedure on the same schema of the tables? If you use toad try to press F4 button when cursor is on table name or try to launch the command "desc tablename"
Try describing the tablename with same credentials in the same schema with which you are trying to compile/execute the function.
desc tablename

Resources