【问题标题】:Oracle SQL: Most efficient way to calculate Z-score of grouped dataOracle SQL:计算分组数据 Z 分数的最有效方法
【发布时间】:2012-12-25 09:46:10
【问题描述】:

我有两张桌子: DATA

DATA_ID  |  SAMPLE_ID  |  ASSAY_ID  |  SIGNAL
101      |  201        |  301       |  2.87964
102      |  201        |  302       |  7.64623
103      |  202        |  301       |  1.98473
...

还有SAMPLES:

SAMPLE_ID  |  SAMPLE_NAME  |  CATEGORY
201        |  SAMP0001     |  CAT A  
202        |  SAMP0002     |  CAT B
203        |  SAMP0003     |  CAT A
...

SAMPLES 中有大约 20,000 行。对于每个样本,DATA 中大约有 40,000 行。每个ASSAY_IDDATA 中的每个样本仅出现一次。我需要获取SAMPLE 中的样本子集,并为DATA 中的每个信号值计算标准/z 分数值,并按ASSAY_ID 分组。我正在尝试创建一个将被重复调用的存储过程,它将接受单个 ASSAY_ID 值并为预定义样本子集中的所有样本返回 SAMPLE_IDZSCORE 对。

给定给定测定的一组样本信号值 (X = [3.21, 4.56, 1.12, ..]),在这种情况下,标准/z 分数计算为

(X[i] - median(X))/(K * MAD)

其中K 是比例因子,等于 1.4826,MAD 是调整后的中值偏差,等于:

median(|X[i]-median(X)|)

明白了吗?好 :) 现在,使用 SQL 查询执行此计算的最有效方法是什么?执行时间是关键,因为 DATA 中有近十亿行,并且几乎每个 SIGNAL 值都需要计算 z 分数。

这是迄今为止我能想到的最好的查询:

WITH BASE AS (
    SELECT 
        S.SAMPLE_ID,
        D.SIGNAL
    FROM
        DATA D
        JOIN SAMPLES S
            ON D.SAMPLE_ID = S.SAMPLE_ID
    WHERE 
        S.CATEGORY IN ('CAT A', 'CAT B')
        AND D.ASSAY_ID = 12345
        AND S.SAMPLE_NAME NOT IN ('SAMP0003', 'SAMP0005', 'SAMP0008')          
)
SELECT  
    A.SAMPLE_ID,
    (A.SIGNAL-B.MED)/(1.4826*C.MAD) AS ZSCORE
FROM 
    BASE A,
    (
        SELECT MEDIAN(X.SIGNAL) AS MED 
        FROM BASE X
    ) B,
    (
        SELECT MEDIAN(ABS(Y.SIGNAL-YY.MED)) AS MAD 
        FROM BASE Y, 
        (SELECT MEDIAN(SIGNAL) AS MED FROM BASE) YY
    ) C 

有没有更有效的方法来执行这个查询?

额外问题:我可以编写一个 SQL 查询,在一次执行中为每个 ASSAY_ID 执行此计算吗?

【问题讨论】:

    标签: sql oracle plsql statistics


    【解决方案1】:

    你能看一下吗:

    SELECT ASSAY_ID, SAMPLE_ID, 
           (SIGNAL - MED)/(1.4826F * MAD) AS ZSCORE
      FROM (
            SELECT ASSAY_ID, SAMPLE_ID, SIGNAL, MED,
                   MEDIAN(ABS(SIGNAL - MED)) OVER (PARTITION BY ASSAY_ID) AS MAD
              FROM (
                    SELECT ASSAY_ID, SAMPLE_ID, SIGNAL,
                           MEDIAN(SIGNAL) OVER (PARTITION BY ASSAY_ID) AS MED
                      FROM DATA    D
                      JOIN SAMPLES S USING (SAMPLE_ID)
                     WHERE S.CATEGORY IN ('CAT A', 'CAT B')
                       AND S.SAMPLE_NAME NOT IN ('SAMP0003', 'SAMP0005', 'SAMP0008')  
                       AND D.ASSAY_ID = 301
                   )
           );
    

    正确吗?它更快吗?如果是,只需删除 额外问题AND D.ASSAY_ID = 301 子句 :-)

    在物理方面,我会研究信号的数据类型(BINARY_FLOATBINARY_DOUBLE 应该比NUMBER 快)。而且,如果这是一个选项,我会尝试将化验与分区物理搭配。

    【讨论】:

    • 谢谢,我明天上班时试试这个!假设这更快,为什么会这样?
    • 没有正确的数据我猜了很多,但看起来WITH 方法将基本查询具体化为一个临时表,该表被完全扫描了四次。分析函数方法似乎可以通过对表DATA 进行一次完整扫描来摆脱困境。
    • 一些快速测试表明这个版本的查询速度稍快,查询计划显示成本有小幅下降。同样重要的是,这是更简洁的代码,也是对奖励问题的一个很好的回答。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2013-08-15
    • 2011-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-03
    • 2021-04-11
    • 1970-01-01
    相关资源
    最近更新 更多