where子句中的奇怪随机行为

Far*_*nov 20 sql oracle plsql

我有这样一张桌子:

     Id | GroupId | Category
     ------------------------
     1  | 101     | A
     2  | 101     | B
     3  | 101     | C
     4  | 103     | B
     5  | 103     | D
     6  | 103     | A
     ........................
Run Code Online (Sandbox Code Playgroud)

我需要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;
Run Code Online (Sandbox Code Playgroud)

因为我舍入了随机值,所以它将是一个整数值,WHERE RN=Round(dbms_random.value(1, v_group_count))条件必须始终返回一行.一般来说它按预期给我一行.但有时奇怪的是它没有给我任何行,有时会返回两行.这就是为什么它在本节中给出了错误:

SELECT GroupId into v_group_id
Run Code Online (Sandbox Code Playgroud)

谁知道这种行为的原因?

Dav*_*itz 23

round(dbms_random.value(1, v_group_count)) 正在为每一行执行,因此可以选择是否每一行.


PS

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
Run Code Online (Sandbox Code Playgroud)
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
;
Run Code Online (Sandbox Code Playgroud)
    N   COUNT(*)
    1   5488
    2   11239
    3   11236
    4   10981
    5   11205
    6   11114
    7   11211
    8   11048
    9   10959
    10  5519
Run Code Online (Sandbox Code Playgroud)

我的建议是在dbms_random.value(1,N + 1)上使用FLOOR

    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   
    ;              
Run Code Online (Sandbox Code Playgroud)
N   COUNT(*)
1   10091
2   10020
3   10020
4   10021
5   9908
6   10036
7   10054
8   9997
9   9846
10  10007              
Run Code Online (Sandbox Code Playgroud)

  • @FaridImranov为证明这一点,您可以将其移动到交叉连接,以便为所有记录生成1次; 如果问题消失了......你可能有答案 (2认同)

Gor*_*off 8

如果您想随机选择一个:

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;
Run Code Online (Sandbox Code Playgroud)