Is there a way to determine the maximum possible value of a pls_integer either by a language predefined constant or a function? I can find the maximum on the internet (2^31 - 1 = 2,147,483,647), but I don't want to hard code it.
Cheers :)
I don't think this is possible. Why? Because it is not needed - PLS_INTEGER's maximal value is due to its maximal size - 4 bytes (and it is a signed datatype).
What is more, as stated in documentation about PL/SQL datatypes, PLS_INTEGER is actually a BINARY_INTEGER. Look at the definition of PLS_INTEGER in the Oracle's STANDARD package:
subtype pls_integer is binary_integer;
And then take a look at the definition of BINARY_INTEGER:
subtype BINARY_INTEGER is INTEGER range '-2147483647'..2147483647;
Nowhere in the STANDARD package header can you find a constant which holds the maximal value of those datatypes.
I don't think there is any constant that you can use; however, if it is so vital not to hard code any values, you can calculate the maximum value with the method given below.
This solution is based on the assumption that the maximum value would be in the form of 2^b-1 where b is the number of bits.
This is the function you can use:
CREATE FUNCTION MAX_PLS_INTEGER_SIZE RETURN PLS_INTEGER AS
p PLS_INTEGER;
b NUMBER;
BEGIN
b := 0;
WHILE TRUE LOOP
BEGIN
p := POWER(2, b)-1;
b := b + 1;
EXCEPTION WHEN OTHERS THEN
EXIT;
END;
END LOOP;
RETURN p;
end;
After you create the function, you can test it:
SELECT MAX_PLS_INTEGER_SIZE FROM DUAL;
Result:
MAX_PLS_INTEGER_SIZE
--------------------
2147483647
Related
I read about IN OUT and NOCOPY. Then I encountered NOCOPY use cases but I was not able to get it. Can anybody explain these with examples? Thanks in advance.
The actual parameter must be implicitly converted to the data type of the formal parameter.
The actual parameter is the element of a collection.
The actual parameter is a scalar variable with the NOT NULL constraint.
The actual parameter is a scalar numeric variable with a range, size, scale, or precision constraint.
The actual and formal parameters are records, one or both was declared with %ROWTYPE or %TYPE, and constraints on corresponding fields differ.
The actual and formal parameters are records, the actual parameter was declared (implicitly) as the index of a cursor FOR LOOP statement, and constraints on corresponding fields differ.
The subprogram is invoked through a database link or as an external subprogram.
The basic principle is that PL/SQL will honour the NOCOPY directive as long as the value we pass can be used as provided, without transformation, and is addressable by the called program. The scenarios you list are circumstances where this is not the cases. I must admit a couple of these examples made me think, so this is a worthwhile exercise.
The first four examples call this toy procedure.
create or replace procedure tst2 (p1 in out nocopy t34%rowtype) is
begin
p1.id := 42;
end;
/
Case 1: The actual parameter must be implicitly converted to the data type of the formal parameter.
declare
n varchar2(3) := '23';
begin
tst(n);
dbms_output.put_line(n);
end;
/
Case 2: The actual parameter is the element of a collection.
declare
nt sys.odcinumberlist := sys.odcinumberlist(17,23,69);
begin
tst(nt(2));
dbms_output.put_line(to_char(nt(2)));
end;
/
Case 3: The actual parameter is a scalar variable with the NOT NULL constraint.
declare
n number not null := 23;
begin
tst(n);
dbms_output.put_line(to_char(n));
end;
/
Case 4: The actual parameter is a scalar numeric variable with a range, size, scale, or precision constraint.
declare
n number(5,2) := 23;
begin
tst(n);
dbms_output.put_line(to_char(n));
end;
/
The next example uses this table ...
create table t34 (id number not null, col1 date not null)
/
...and toy procedure:
create or replace procedure tst2 (p1 in out nocopy t34%rowtype) is
begin
p1.id := 42;
end;
/
Case 5 : The actual and formal parameters are records, one or both was declared with %ROWTYPE or %TYPE, and constraints on corresponding fields differ.
declare
type r34 is record (id number, dt date);
r r34;
begin
r.id := 23;
r.dt := to_date(null); --trunc(sysdate);
tst2(r);
dbms_output.put_line(to_char(r.id));
end;
/
The next example uses this package spec...
create or replace package pkg is
type r34 is record (id number, dt date);
end;
/
...and toy procedure:
create or replace procedure tst3 (p1 in out nocopy pkg.r34) is
begin
p1.id := p1.id + 10;
end;
/
Case 6: The actual and formal parameters are records, the actual parameter was declared (implicitly) as the index of a cursor FOR LOOP statement, and constraints on corresponding fields differ.
begin
for j in ( select * from t34) loop
tst3(j);
dbms_output.put_line(to_char(j.id));
end loop;
end;
/
The last example uses a remote version of the first procedure.
Case 7: The subprogram is invoked through a database link or as an external subprogram.
declare
n number := 23;
begin
tst#remote_db(n);
dbms_output.put_line(to_char(n));
end;
/
There are working demos of the first six cases on db<>fiddle here.
I am trying to create a way to find the exponent of a number (in this case the base is 4 and the exponent 2 so the answer should be 16) using a procedure without using the POW Function or any built in functions to find the exponent. Eventually I would like to take input numbers from the user.
set serveroutput on;
CREATE OR REPLACE PROCEDURE Exponent(base number, exponent number) as
answer number;
BEGIN
base := 4;
exponent := 2;
LOOP
IF exponent > 1 THEN
answer := base * base;
END IF;
END LOOP;
dbms_output.put_line('Answer is: ' || answer);
END;
/
Error(7,25): PLS-00103: "expression 'BASE' cannot be used as an assignment target" and "expression 'EXPONENT' cannot be used as an assignment target"
Any ideas on how to solve the error and/or better ways of getting the exponent without using built-in functions like POW?
In your procedure base and exponent are input parameters and can't be changed. You've got a couple of options:
1) copy the parameters to variables internal to the procedure and manipulate those internal values, or
2) change the parameters to be input/output parameters so you can change them.
Examples:
1)
CREATE OR REPLACE PROCEDURE Exponent(pin_base number, pin_exponent number) as
base number := pin_base;
exponent number := pin_exponent;
answer number;
BEGIN
base := 4;
exponent := 2;
LOOP
IF exponent > 1 THEN
answer := base * base;
END IF;
END LOOP;
dbms_output.put_line('Answer is: ' || answer);
END;
2)
CREATE OR REPLACE PROCEDURE Exponent(base IN OUT number,
exponent IN OUT number) as
answer number;
BEGIN
base := 4;
exponent := 2;
LOOP
IF exponent > 1 THEN
answer := base * base;
END IF;
END LOOP;
dbms_output.put_line('Answer is: ' || answer);
END;
The best thing is that whatever Oracle provides as inbuilt functionality that serves the purpose in a best possible. (Almost all the times better then customized codes) Try to use EXP function. I have tried to make customized code per my understanding. Hope this helps.
CREATE OR REPLACE
FUNCTION EXP_DUMMY(
BASE_IN IN NUMBER,
EXPO_IN IN NUMBER)
RETURN PLS_INTEGER
AS
lv PLS_INTEGER:=1;
BEGIN
FOR I IN
(SELECT base_in COL1 FROM DUAL CONNECT BY level < expo_in+1
)
LOOP
lv:=lv*i.col1;
END LOOP;
RETURN
CASE
WHEN EXPO_IN = 0 THEN
1
ELSE
lv
END;
END;
SELECT EXP_DUMMY(2,4) FROM DUAL;
I understand that the index variable/counter for the PL/SQL For loop is defined implicitly by the loop construct
BEGIN
FOR v_counter IN 1..5 LOOP
DBMS_OUTPUT.PUT_LINE ('v_counter = '||v_counter);
END LOOP;
END;
What is the datatype of this variable. Tempted to say BINARY_INTEGER or PLS_INTEGER as this would allow for negative values of counters too , and both perform better as far as calculations are concerned.
Is this inference right ? Are there any other considerations ?
The documentation here:
http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/for_loop_statement.htm#LNPLS1536
States simply "integer".
Previous thread here:
What's the difference between pls_integer and binary_integer?
Points out that binary_integer = pls_integer.
So it likely doesn't matter, since they (now) behave the same.
I'm attempting to replicate the functionality of an Excel workbook in Oracle. One of the formulae on the worksheet uses CHIDIST, which "Returns the one-tailed probability of the chi-squared distribution". I can't see a similar function in Oracle anywhere! Am I going to have to write my own CHIDIST function?
Here's a link to Excel's documentation on CHIDIST: http://office.microsoft.com/en-gb/excel-help/chidist-HP005209010.aspx
I've already worked out the Chi Square and Degrees of Freedom.
Thanks
If you're using CHIDIST to do a chi-squared test, the STATS_CROSSTAB function might be a different means to the same end. It will calculate the chi-squared value, significance, or degrees of freedom for two sets of paired observations.
Right tail probability... nice...
In oracle you can embedded java code into stored procedures and functions. The best way is to
use proper java class and call it from oracle's function. It is not that hard:
http://docs.oracle.com/cd/B19306_01/java.102/b14187/chfive.htm
http://jwork.org/scavis/api/doc.php/umontreal/iro/lecuyer/probdist/ChiDist.html
That is all I can do for you:
DECLARE
l_value_to_evaluate NUMBER (10, 3) := 18.307; /* X; Value at which you want to evaluate the distribution */
l_degrees_freedom NUMBER := 10; /* Degrees of freedom */
l_number NUMBER;
FUNCTION is_number(str_in IN VARCHAR2) RETURN NUMBER
IS
n NUMBER;
BEGIN
n := TO_NUMBER(str_in);
RETURN 1;
EXCEPTION
WHEN VALUE_ERROR THEN
RETURN 0;
END;
BEGIN
-- If either argument is nonnumeric, CHIDIST returns the #VALUE! error value.
l_number := is_number(l_value_to_evaluate);
l_number := is_number(l_degrees_freedom);
-- If x is negative, CHIDIST returns the #NUM! error value.
IF SIGN(l_value_to_evaluate) = -1 THEN
RAISE_APPLICATION_ERROR(-20998, '#NUM!');
END IF;
-- If degrees_freedom is not an integer, it is truncated.
l_degrees_freedom := TRUNC(l_degrees_freedom);
-- If degrees_freedom < 1 or degrees_freedom > 10^10, CHIDIST returns the #NUM! error value.
IF l_degrees_freedom < 1
OR l_degrees_freedom > POWER(10, 10) THEN
RAISE_APPLICATION_ERROR(-20997, '#NUM!');
END IF;
-- CHIDIST is calculated as CHIDIST = P(X>x), where X is a χ2 random variable.
/* Here the integral's implementation */
EXCEPTION
WHEN VALUE_ERROR THEN
RAISE_APPLICATION_ERROR(-20999, '#VALUE!');
END;
I've inherited some code which is going to be the base for some additional work. Looking at the stored procs, I see quite a lot of associative-arrays.
Some of these are indexed by binary_integers, some by pls_integers. Are there any differences between the two?
I had a look at the documentation, but apart from this line:
The PL/SQL data types PLS_INTEGER and BINARY_INTEGER are identical. For simplicity, this document uses PLS_INTEGER to mean both PLS_INTEGER and BINARY_INTEGER.
I couldn't find any difference between the two. So what's the difference? Are both around for historical/compatibility reasons?
I'm using Oracle 10gR2
Historical reasons. They used to be different before 10g:
On 8i and 9i, PLS_INTEGER was noticeably faster than BINARY_INTEGER.
When it comes to declaring and manipulating integers, Oracle offers lots of options, including:
INTEGER - defined in the STANDARD package as a subtype of NUMBER, this datatype is implemented in a completely platform-independent fashion, which means that anything you do with NUMBER or INTEGER variables should work the same regardless of the hardware on which the database is installed.
BINARY_INTEGER - defined in the STANDARD package as a subtype of INTEGER. Variables declared as BINARY_INTEGER can be assigned values between -231+1 .. 231-1, aka -2,147,483,647 to 2,147,483,647. Prior to Oracle9i Database Release 2, BINARY_INTEGER was the only indexing datatype allowed for associative arrays (aka, index-by tables), as in:
TYPE my_array_t IS TABLE OF VARCHAR2(100)
INDEX BY BINARY_INTEGER
PLS_INTEGER - defined in the STANDARD package as a subtype of BINARY_INTEGER. Variables declared as PLS_INTEGER can be assigned values between -231+1 .. 231-1, aka -2,147,483,647 to 2,147,483,647. PLS_INTEGER operations use machine arithmetic, so they are generally faster than NUMBER and INTEGER operations. Also, prior to Oracle Database 10g, they are faster than BINARY_INTEGER. In Oracle Database 10g, however, BINARY_INTEGER and PLS_INTEGER are now identical and can be used interchangeably.
binary_integer and pls_integer both are same. Both are PL/SQL datatypes with range -2,147,648,467 to 2,147,648,467.
Compared to integer and binary_integer pls_integer very fast in excution. Because pls_intger operates on machine arithmetic and binary_integer operes on library arithmetic.
pls_integer comes from oracle10g.
binary_integer allows indexing integer for assocative arrays prior to oracle9i.
Clear example:
SET TIMING ON
declare
num integer := 0;
incr integer := 1;
limit integer := 100000000;
begin
while num < limit loop
num := num + incr;
end loop;
end;
PL/SQL procedure successfully completed.
Elapsed: 00:00:20.23
ex:2
declare
num binary_integer := 0;
incr binary_integer := 1;
limit binary_integer := 100000000;
begin
while num < limit loop
num := num + incr;
end loop;
end;
/
PL/SQL procedure successfully completed.
Elapsed: 00:00:05.81
ex:3
declare
num pls_integer := 0;
incr pls_integer := 1;
limit pls_integer := 100000000;
begin
while num < limit loop
num := num + incr;
end loop;
end;
/
Another difference between pls_integer and binary_integer is that when calculations involving a pls_integer overflow the PL/SQL engine will raise a run time exception. But, calculations involving a binary_integer will not raise an exception even if there is an overflow.