Подтвердить что ты не робот

Странное случайное поведение в where where

У меня есть таблица вроде этого:

     Id | GroupId | Category
     ------------------------
     1  | 101     | A
     2  | 101     | B
     3  | 101     | C
     4  | 103     | B
     5  | 103     | D
     6  | 103     | A
     ........................

Мне нужно выбрать один из GroupId случайным образом. Для этого я использовал следующий блок кода PL/SQL:

declare v_group_count number;
  v_group_id number;
begin 
  select count(distinct GroupId) into v_group_count from MyTable;
  SELECT GroupId into v_group_id  FROM
  (
    SELECT GroupId, ROWNUM RN FROM 
    (SELECT DISTINCT GroupId FROM MyTable)
  )
  WHERE RN=Round(dbms_random.value(1, v_group_count));
end;

Поскольку я округлял случайное значение, тогда это будет целочисленное значение, а условие WHERE RN=Round(dbms_random.value(1, v_group_count)) должно всегда возвращать одну строку. Как правило, это дает мне одну строку, как ожидалось. Но, как ни странно, иногда это не дает мне никаких строк, а иногда возвращает две строки.. Поэтому он дает ошибку в этом разделе:

SELECT GroupId into v_group_id

Кто-нибудь знает причину такого поведения?

4b9b3361

Ответ 1

round(dbms_random.value(1, v_group_count)) выполняется для каждой строки, поэтому каждая строка может быть выбрана или нет.


P.s.

ROUND - плохой выбор.

Вероятность получения любого из значений кромки (например, 1 и 10) составляет половину вероятности получения любого другого значения (например, от 2 до 9).
Это 0.0555... (1/18) Vs. 0.111... (1/9)

[  1,1.5) --> 1
[1.5,2.5) --> 2
.
.
.
[8.5,9.5) --> 9
[9.5, 10) --> 10

select          n,count(*)
from           (select          round(dbms_random.value(1, 10)) as n
                from            dual
                connect by      level <= 100000
                )
group by        n
order by        n
;

    N   COUNT(*)
    1   5488
    2   11239
    3   11236
    4   10981
    5   11205
    6   11114
    7   11211
    8   11048
    9   10959
    10  5519

Моя рекомендация - использовать FLOOR для dbms_random.value(1, N + 1)

    select          n,count(*)
    from           (select          floor(dbms_random.value(1, 11)) as n
                    from            dual
                    connect by      level <= 100000
                    )
    group by        n
    order by        n   
    ;              

N   COUNT(*)
1   10091
2   10020
3   10020
4   10021
5   9908
6   10036
7   10054
8   9997
9   9846
10  10007              

Ответ 2

Если вы хотите выбрать одно случайно:

declare v_group_count number;
  v_group_id number;
begin 
  SELECT GroupId into v_group_id
  FROM (SELECT DISTINCT GroupId
        FROM MyTable
        ORDER BY dbms_random.value
       ) t
  WHERE rownum = 1
end;