【问题标题】:Random join in oracle随机加入oracle
【发布时间】:2012-07-22 22:38:11
【问题描述】:

我有三个查询,我希望由这些查询的条目组成的结果行随机连接在一起。

我不想合并结果,而是以或多或少随机的方式加入它们(可以保留原始分布,也可以在所有结果中统一)。

我尝试了以下方法:

select * 
from 
(
    SELECT street, number 
    FROM Addresses 
    WHERE valid = '1' 
    order by Dbms_Random.Value
) q1 ,
(
    select prename 
    from person 
    order by Dbms_Random.Value
) q2 , 
(
    select surname 
    from person 
    order by Dbms_Random.Value
) q3

我的结果集看起来一点也不随机:

Main street, 1, Andre, Smith
Main street, 1, Andre, Warnes
Main street, 1, Andre, Jackson
Main street, 1, Andre, Macallister

从查询中删除 ORDER BY 并将其应用于笛卡尔积的结果效率极低,因为表很大,尤其是它们的笛卡尔积。

【问题讨论】:

  • 当前您正在检索笛卡尔积。那是你要的吗?就像,如果每个表有 10 行,那么您是否希望您的结果集包含所有 1000 个连接行,只是以随机顺序排列?还是只需要 10 行?
  • 您要查找多少个值?每张桌子有多大?
  • 感谢您的回答。结果的总数不应该是笛卡尔积的大小,而是可以介于最小表的大小和所涉及的最大表的大小之间。一个表中的行出现多次是可以的。

标签: sql oracle join random


【解决方案1】:

Colin 't Hart 诊断出问题,并建议使用 rownum 解决问题。但是解决方案稍微复杂一点,因为如果它们都出现在同一个 SELECT 中,ROWNUM 是在 ORDER BY 之前分配的。解决方案是添加一个额外的子查询级别。

with randomAddress as(
  select rownum id, street, num from (
    select * from addresses where valid=1 order by dbms_random.random
  )
),
randomPrename as(
  select rownum id, prename from(
    select * from person order by dbms_random.random
  )
),
randomSurname as(
  select rownum id, surname from(
    select * from person order by dbms_random.random
  )
)
select street, num, prename, surname
  from randomAddress
  join randomPrename using(id)
  join randomSurname using(id)
;

此解决方案将始终返回与最小表中的行数相等的随机行数。任何行都不会被使用超过一次。这是SQL Fiddle

GWu 解决方案返回的行数将根据分配相同随机数的行数而有所不同。有些行可能会被多次使用,而其他行则根本不用。您还应该知道要使用该解决方案的表中有多少行。

【讨论】:

    【解决方案2】:

    您可以将 Dbms_Random.Value 移动到子查询中的列并通过它加入。
    这将使结果随机化,并摆脱order by

    select * 
      from 
            (
                SELECT street, snumber, ROUND(Dbms_Random.Value(1,10)) n
                  FROM Addresses 
                 WHERE valid = '1' 
            ) q1 ,
            (
                select prename, ROUND(Dbms_Random.Value(1,10)) n 
                  from person 
            ) q2 , 
            (
                select surname, ROUND(Dbms_Random.Value(1,10)) n
                  from person 
            ) q3
    where q1.n = q2.n
      and q2.n = q3.n
    ;
    

    (另见http://www.sqlfiddle.com/#!4/a26d0/9

    ROUND(Dbms_Random.Value(1,10)) 中的值 10 只是一个假设,将其更改为您的预期或可用记录数。

    请注意,此解决方案会重复使用每个子查询的结果,因此例如 prename 可能会多次使用或根本不使用,但在您的原始笛卡尔连接中也是如此。
    Colin's approach 确保唯一性,如果你需要那个。

    【讨论】:

    • 返回的行数是随机的。从理论上讲,这种解决方案有时可能不会返回任何行,尽管这是极低的风险,除非随机数限制远大于最小表中的行数。
    【解决方案3】:

    您遇到的问题是,虽然每个表都是随机排序的,但您仍然有一个笛卡尔积,因此顶部行的前两列具有相同的值,只有最后一列不同。

    如果您选择伪列 ROWNUM(您需要将其别名为例如 row_number),然后在 row_number 上连接 3 个表,您应该从 3 个表中获得随机数据组合。

    但总行数将被限制为等于最小表中的行数。

    【讨论】:

    • +1,但是解决方案比您描述的要复杂一些。见my answer
    • OP 需要澄清他是否希望值只使用一次。
    猜你喜欢
    • 2016-02-24
    • 2011-12-06
    • 2020-10-11
    • 1970-01-01
    • 2017-04-07
    • 2017-09-23
    • 1970-01-01
    • 1970-01-01
    • 2017-01-03
    相关资源
    最近更新 更多