Re: Force randomize within a set of numbers
Date: Fri, 4 Dec 2009 07:12:50 -0800 (PST)
Message-ID: <78ea86f1-80bf-44fd-96f2-10091453a854_at_o9g2000prg.googlegroups.com>
On Dec 3, 5:01 pm, CrazyKarma <ska..._at_gmail.com> wrote:
> Here is the last one for this week..I guess :)
>
> Is there way to force the randomize within a set of number in Oracle?
> Say I have a set of integers ( 2,8,6,5)
> and I want to force randomize function to randomly pick within that
> set only.
>
> I know about the DBMS_RANDOM package, sample and seed clause but none
> help what I need to do here.
>
> any thoughts?
>
> CK
Here is another way to do it, if you do not know how many elements will be in the list.
First, we return a row from DUAL with the rows of interest with a
comma appended at the start and end:
SELECT
  ',2,8,6,5,54,100,67,7778,6,' ITEMS
FROM
  DUAL;
ITEMS
,2,8,6,5,54,100,67,7778,6,
Next, we need to determine the number of elements and pick an element
position at random:
SELECT
  ITEMS,
  SUM(SIGN(INSTR(ITEMS, ',',1,ROWNUM)))-1 NUM_ITEMS,
  (TRUNC(DBMS_RANDOM.VALUE(0,SUM(SIGN(INSTR(ITEMS ,',',1,ROWNUM)))-1))
+1) SEL_ITEM
FROM
  (SELECT
    ',2,8,6,5,54,100,67,7778,6,' ITEMS
  FROM
    DUAL)
CONNECT BY
  LEVEL<20;
ITEMS NUM_ITEMS SEL_ITEM -------------------------- ---------- ---------- ,2,8,6,5,54,100,67,7778,6, 9 6
Finally, we push the above SQL statement into an inline view, search
for the specified number of commas according to SEL_ITEM column to
determine the starting position of the element, and then search for
the next comma to determine the ending position of the element:
SELECT
  ITEMS,
  SEL_ITEM,
  SUBSTR(ITEMS,INSTR(ITEMS,',',1,SEL_ITEM)+1,(INSTR(ITEMS,',',
1,SEL_ITEM+1)) - (INSTR(ITEMS,',',1,SEL_ITEM)) -1) ITEM
FROM
(SELECT
  ITEMS,
  SUM(SIGN(INSTR(ITEMS, ',',1,ROWNUM)))-1 NUM_ITEMS,
  (TRUNC(DBMS_RANDOM.VALUE(0,SUM(SIGN(INSTR(ITEMS ,',',1,ROWNUM)))-1))
+1) SEL_ITEM
FROM
  (SELECT
    ',2,8,6,5,54,100,67,7778,6,' ITEMS
  FROM
    DUAL)
CONNECT BY
  LEVEL<20);
ITEMS SEL_ITEM ITEM -------------------------- ---------- ---- ,2,8,6,5,54,100,67,7778,6, 6 100
--
You might also be able to do something like this:
SELECT
  '2,8,6,5,54,100,67,7778,6' ITEMS,
  DBMS_RANDOM.VALUE(0,1) PERCENT
FROM
  DUAL;
ITEMS                       PERCENT
------------------------ ----------
2,8,6,5,54,100,67,7778,6 .582165524
SELECT
  ROWNUM ITEM_NUMBER,
  REGEXP_SUBSTR(ITEMS,'\w+',1,LEVEL) ITEM,
  PERCENT
FROM
  (SELECT
    '2,8,6,5,54,100,67,7778,6' ITEMS,
    DBMS_RANDOM.VALUE(0,1) PERCENT
  FROM
    DUAL)
CONNECT BY
  REGEXP_SUBSTR(ITEMS,'\w+',1,LEVEL) IS NOT NULL;
ITEM_NUMBER ITEM     PERCENT
----------- ----- ----------
          1 2     .104480002
          2 8      .81670697
          3 6     .826051929
          4 5     .477132421
          5 54     .89077554
          6 100   .640842927
          7 67    .145088893
          8 7778  .252241096
          9 6     .490905924
As you can see from the above, we have a problem in that the random
percent changes for each row, which will cause a problem for us if we
try to use it in a WHERE clause.
SELECT
  MAX(ITEM_NUMBER) OVER () NUM_ITEMS,
  PERCENT_RANK() OVER (ORDER BY ITEM_NUMBER) PR,
  ITEM_NUMBER,
  ITEM,
  PERCENT
FROM
(SELECT
  ROWNUM ITEM_NUMBER,
  REGEXP_SUBSTR(ITEMS,'\w+',1,LEVEL) ITEM,
  PERCENT
FROM
  (SELECT
    '2,8,6,5,54,100,67,7778,6' ITEMS,
    DBMS_RANDOM.VALUE(0,1) PERCENT
  FROM
    DUAL)
CONNECT BY
  REGEXP_SUBSTR(ITEMS,'\w+',1,LEVEL) IS NOT NULL);
NUM_ITEMS         PR ITEM_NUMBER ITEM     PERCENT
--------- ---------- ----------- ----- ----------
        9          0           1 2     .110718377
        9       .125           2 8     .306241972
        9        .25           3 6     .953005936
        9       .375           4 5     .033518415
        9         .5           5 54    .803485415
        9       .625           6 100   .456278133
        9        .75           7 67     .04461405
        9       .875           8 7778  .249680394
        9          1           9 6     .484834331
If we now use a FIRST_VALUE analytic function, we could just retrieve
the first PERCENT value and use that in a WHERE clause:
SELECT
  NUM_ITEMS,
  ITEM_NUMBER,
  ITEM
FROM
(SELECT
  MAX(ITEM_NUMBER) OVER () NUM_ITEMS,
  ITEM_NUMBER,
  ITEM,
  FIRST_VALUE(PERCENT) OVER () PERCENT
FROM
(SELECT
  ROWNUM ITEM_NUMBER,
  REGEXP_SUBSTR(ITEMS,'\w+',1,LEVEL) ITEM,
  PERCENT
FROM
  (SELECT
    '2,8,6,5,54,100,67,7778,6' ITEMS,
    DBMS_RANDOM.VALUE(0,1) PERCENT
  FROM
    DUAL)
CONNECT BY
  REGEXP_SUBSTR(ITEMS,'\w+',1,LEVEL) IS NOT NULL))
WHERE
  ITEM_NUMBER=(TRUNC(NUM_ITEMS*PERCENT)+1);
NUM_ITEMS ITEM_NUMBER ITEM
--------- ----------- ----
        9           7 67
Charles Hooper
Co-author of "Expert Oracle Practices: Oracle Database Administration
from the Oak Table"
http://hoopercharles.wordpress.com/
IT Manager/Oracle DBA
K&M Machine-Fabricating, Inc.
Received on Fri Dec 04 2009 - 09:12:50 CST
