这是我对堆栈溢出的第一个问题。
我们正在对模拟数据进行统计,我想在Oracle-SQL中实现引导程序(带替换的采样数据)。我的做法如下:
但是,连接似乎有问题。 Oracle 正在丢失一些行。
这是一个例子。当我执行以下查询时,我得到行的随机排序:
WITH basis as (
SELECT 'A' AS KPI_KEY, 2 AS KPI_VALUE FROM dual
union all
SELECT 'A' AS KPI_KEY, 5 AS KPI_VALUE FROM dual
union all
SELECT 'A' AS KPI_KEY, 3 AS KPI_VALUE FROM dual
), group_counts AS (
SELECT KPI_KEY, COUNT(*) as total_count
FROM basis
GROUP BY KPI_KEY
)
, random_numbers AS (
SELECT KPI_KEY, ceil(dbms_random.value(0, max(total_count))) AS rand_rn
FROM group_counts
GROUP BY KPI_KEY
CONNECT BY LEVEL <= total_count AND PRIOR KPI_KEY = KPI_KEY AND PRIOR dbms_random.value IS NOT NULL
)
select * from random_numbers
;
结果(正确): KPI_KEY RAND_RN ‘A’1 'A2 ‘A’2
现在,当我像下面这样连接编号行时,结果是错误的:
WITH basis as (
SELECT 'A' AS KPI_KEY, 2 AS KPI_VALUE FROM dual
union all
SELECT 'A' AS KPI_KEY, 5 AS KPI_VALUE FROM dual
union all
SELECT 'A' AS KPI_KEY, 3 AS KPI_VALUE FROM dual
), group_counts AS (
SELECT KPI_KEY, COUNT(*) as total_count
FROM basis
GROUP BY KPI_KEY
)
, numbered_rows AS (
SELECT KPI_KEY, KPI_VALUE, ROW_NUMBER()
OVER (PARTITION BY KPI_KEY ORDER BY dbms_random.value) AS rn, total_count
FROM basis
JOIN group_counts USING (KPI_KEY)
)
, random_numbers AS (
SELECT KPI_KEY, ceil(dbms_random.value(0, max(total_count))) AS rand_rn
FROM group_counts
GROUP BY KPI_KEY
CONNECT BY LEVEL <= total_count AND PRIOR KPI_KEY = KPI_KEY AND PRIOR dbms_random.value IS NOT NULL
)
SELECT rn.KPI_KEY, nr.KPI_VALUE, nr.rn
FROM random_numbers rn
LEFT JOIN numbered_rows nr
ON rn.KPI_KEY = nr.KPI_KEY
and rn.rand_rn = nr.rn
;
结果(值是随机的,但连接中缺少行): KPI_KEY KPI_VALUE RN ‘A’5 3
有人遇到过类似的问题吗? 对我来说似乎是 Oracle 的错误,但也许我错过了一个重要的细节。
我尝试了物化提示,以及此处找到的 rownum 方法:Random join in oracle
错误在于,倒数第二个查询的
GROUP BY
将所有生成的行聚合到一行中,而 CONNECT BY
子句实际上做了不必要的工作,收益为零。
这似乎是因为 SQL 引擎在重写查询时,如果您从倒数第二个查询中获取输出,而没有最终查询,那么它会执行
GROUP BY
,然后 CONNECT BY
,如果您同时执行倒数第二个和最终查询,那么它先执行 CONNECT BY
,然后执行 GROUP BY
(以相反的顺序)。您可以在问题底部链接的小提琴中看到 EXPLAIN PLAN
。
可以修复查询并具体化倒数第二个查询的输出,以便
EXPLAIN PLAN
与查询的最终部分一致生成;然而,这似乎是一个脆弱的解决方案,您可能最好完全重写查询,以使用 (a) 更简单和 (b) 受编译器重写查询影响较小的东西。
假设你的逻辑是:
对于每个
kpi_key
:
kpi_key
(使用 CEIL(DBMS_RANDOM.VALUE(0, num_rows))
的逻辑中存在一个错误,因为 DBMS_RANDOM
将包括下限并排除上限因此您可以获取从 0 开始的值(您不想包含该值),然后向上舍入到 num_rows
,但概率不均匀,因为排除了上限;相反,您想使用 FLOOR
并加 1) .自连接行,以便每一行都连接到其随机对应部分。
然后您可以使用分析函数重写查询:
WITH basis (kpi_key, kpi_value) as (
SELECT 'A', 2 FROM dual union all
SELECT 'A', 5 FROM dual union all
SELECT 'A', 3 FROM dual
),
random_numbers AS (
SELECT kpi_key,
kpi_value,
1 + FLOOR(DBMS_RANDOM.VALUE(0, COUNT(*) OVER (PARTITION BY kpi_key))) AS rand_rn,
ROW_NUMBER() OVER (PARTITION BY kpi_key ORDER BY ROWNUM) AS rn
FROM basis
)
select original.kpi_key AS o_kpi_key,
original.kpi_value AS o_kpi_value,
original.rn AS o_rn,
rand.kpi_key AS r_kpi_key,
rand.kpi_value AS r_kpi_value,
rand.rn AS r_rn
from random_numbers original
INNER JOIN random_numbers rand
ON original.rand_rn = rand.rn;
注意:当您将其与随机值进行比较时,没有必要对
ROW_NUMBER
输出进行随机排序,并且将静态排序列表与该列表中的随机值进行比较时,您将获得与比较随机值时相同的随机性。 -有序列表到该列表中的随机值。
可能会随机输出:
O_KPI_KEY | O_KPI_VALUE | O_RN | R_KPI_KEY | R_KPI_VALUE | R_RN |
---|---|---|---|---|---|
A | 5 | 2 | A | 3 | 3 |
A | 2 | 1 | A | 3 | 3 |
A | 3 | 3 | A | 2 | 1 |
注意:您可能只需要与随机行选择相对应的最后 3 列。