Saturday, October 01, 2005

May be now time to test ;)

This will really test our potential...(Just kidding)
How do you create a permutation & combination in sql

A permutation is an ordered arrangement of elements of a set.
For example, if I have the set {1, 2, 3}, the permutations of those elements are
(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), and (3, 2, 1).
The rule is that for n elements, you have a factorial n! number of permutations.

Try to make the answer easy to generalize for more numbers like suppose i give {1,2,3,4,5} i shud get the permutation of the numbers [ in total 5! ].

Let us try a basic one.

           CREATE TABLE Elements (i INTEGER NOT NULL);

i'm trying for the set {1,2,3}.
so inserting three values in the table

INSERT INTO Elements VALUES (1);
INSERT INTO Elements VALUES (2);
INSERT INTO Elements VALUES (3);

don't read more. try at ur hand in machine infront of u,
take it as challenge to do this ;)

FIRST ANSWER
SELECT e1.i A,e2.i B,e3.i C
FROM Elements e1,Elements e2,Elements e3
WHERE e1.i NOT IN (e2.i,e3.i)
AND e2.i NOT IN (e1.i,e3.i)
AND e3.i NOT IN (e2.i,e1.i)

This looks obvious and horrible answer. isn't ?.
This monster predicate will guarantee that all column values
in a row are unique.An improvement on this query can be made
by adding one more predicate to the where clause

FIRST ANSWER [ with modification ]
SELECT e1.i A,e2.i B,e3.i C
FROM Elements e1,Elements e2,Elements e3
WHERE (E1.i + E2.i + E3.i) = 6
AND e1.i NOT IN (e2.i,e3.i)
AND e2.i NOT IN (e1.i,e3.i)
AND e3.i NOT IN (e2.i,e1.i)

This improves things because most optimizers will see a predicate
of the structure [EXPRESSION] = [CONSTANT] and will execute it
before the and-ed chain of in() predicates. While not all rows
that total 6 are a permutation, all permutations will total to 6
for this set of integers.
It will definetly improve the query performance , agreed ?

Do you think this will be the solution ? No ,
we can try to write in different way.
Let's carry the totals trick one step further.
First,
redefine the Elements table to have a weight for each element
in the set: The weights are powers of two of the elements.

CREATE TABLE Elements_with_wgt(
i INTEGER NOT NULL ,
wgt INTEGER NOT NULL);

INSERT INTO Elements_with_wgtVALUES (1, 1);
INSERT INTO
Elements_with_wgtVALUES (2, 2);
INSERT INTO
Elements_with_wgtVALUES (3, 4);

Now, see the where clause how it becomes:

SECOND ANSWER
SELECT e1.i A,e2.i B,e3.i C
FROM Element1 e1,Element1 e2,Element1 e3
WHERE (E1.wgt + E2.wgt + E3.wgt) = 7

This does the whole filtering job for you and the in() predicates
are all unnecessary. In the previous solution we have seen the
elements shud be numbers as we are summming the value & adding
that to predicate, so the elements cant be other than numbers.
But this second answer also has another beneficial effect:
The elements can now be of any data type and
are not limited just to numbers.

A Thought

"You can't be truly rude until you understand good manners."

I read this somewhere . good one :)


A word abt INTEGERS

Recently, I gotta chance to do something which changed my way of understanding .Believed me until that point thought an integer is a number that can only store whole numbers (or more mathematically expressed: the positive natural number 1, 2, 3..., the negative natural numbers -1, -2, -3, as well as zero). Unfortunately, that is not always the case with Oracle.

                declare
i integer;
n number(38);
begin
i := 3.14159265;
n := 2.17111891;
dbms_output.put_line('i: ' || i);
dbms_output.put_line('n: ' || n);
end;
/

try this u will find the expected answer, the variables cannot store
the fractional parts of the numbers being assigned to the variables.
In fact, Oracle rounds the numbers towards the closest natural number:

i: 3
n: 3

Now let us remove the declaration part & try the following
create procedure takes_integer(i in integer, p in pls_integer)
is
begin
dbms_output.put_line('i [integer]: ' || i || ', p [pls_integer]: ' || p);
end takes_integer;
/


Things change slightly, but significantly, when the declaration
is not in the DECLARE part of a PL/SQL block but in the argument
list of a procedure.
Exec this
exec takes_integer(3.14159265, 2.71828183);

This procedure is called, again with two non-natural numbers
did u get surprised by the result
i [integer]: 3.14159265, p [pls_integer]: 3

The argument i an integer can obviously store non integers.
However, a pls_integer can not.

Point to be kept in mind :
if you want to have an integer passed to a procedure/function,
use pls_integer instead of integer. so that u canmake sure that
u are passing only the integer not as non-integer.

how it happened in case of procedure ??
try desc ing the procedure
desc takes_integer;

SQL> desc takes_integer
PROCEDURE takes_integer
Argument Name Type In/Out Default?
------------------------------ ----------------------- ------ --------
I NUMBER(38) IN
P BINARY_INTEGER IN


An integer, being an ansi_datatype , is (rightly) implicitely converted
into number(38).But number(38) should not be able to stored 3.14159265.

R u still using INTEGER or NUMBER for ur looping ?

We always looking to squeeze that extra performance to make our PL/SQL programs, functions and procedures run just a little bit faster?
Hey, you already have them optimized, right ? have u ?

For example in a loop or record counter,
which one u will use NUMBER or INTEGER... what thinking abt answer to this question ?.
if u have answered anyone of this u r wrong.
Answer shud h've been "options not enough"
Still using number or integer for the counter field, throw up those variables , use PLS datatypes.
Well, consider using the PL/SQL datatype pls_integer instead of the number datatype where only integers are needed.
When declaring an integer variable, PLS_INTEGER is the most efficient numeric datatype because its values require less storage than INTEGER or NUMBER values, which are represented internally as 22-byte Oracle numbers. The number datatype is a memory hog compared to pls_integer, which is designed to consume very little resource at runtime, specifically for PL/SQL.
Also, PLS_INTEGER operations use machine arithmetic, so they are faster than BINARY_INTEGER, INTEGER, or NUMBER operations,which use library arithmetic.

Let us see what is this datatype

This datatype is available in PL/SQL Release 2.3 and above.

Variables declared as PLS_INTEGER store signed integers. The magnitude range for this datatype is -2147483647 through 2147483647. Oracle recommends that you use PLS_INTEGER for all integer calculations which do not fall outside of its range. PLS_INTEGER values require less storage than NUMBER values, and operations on PLS_INTEGER's use machine arithmetic, making them more efficient.

Variables declared as pls_integer and binary_integer have the same range, but they are treated differently. When a calculation involving pls_integer overflows, pl/sql raises an exception. However, similar overflow involving binary_integers will not raise an exception if the result is being assigned to a number variable.

Usage :
DECLARE
fast_variable pls_integer;

wait for more posting to get what is " Library / machine arithmetic " .