从 Snowflake 中的表中选择随机百分比(使用 WHERE 子句时)

Posted

技术标签:

【中文标题】从 Snowflake 中的表中选择随机百分比(使用 WHERE 子句时)【英文标题】:Select random percentage from a table in Snowflake (while using the WHERE clause) 【发布时间】:2021-01-09 01:29:19 【问题描述】:

使用此页面作为指南:https://docs.snowflake.com/en/sql-reference/constructs/sample.html

对于本练习,我需要将表中的部分记录拆分为 50/50:

这些工作。我得到了几乎 50% 的表格行数:

SELECT * FROM MyTable SAMPLE (50);
SELECT * FROM MyTable TABLESAMPLE (50);

一旦我应用了 WHERE 子句,SAMPLE 就不再起作用了:

SELECT * FROM MyTable
WHERE country = ‘USA’ 
AND load_date = CURRENT_DATE
SAMPLE (50);

这让我从上面的雪花页面看到了这个:

方法一;将样本应用于其中一个联接表

select i, j 
    from table1 as t1 inner join table2 as t2 sample (50)
    where t2.j = t1.i 
    ;

方法二;将样本应用于连接表的结果

select * 
   from ( 
         select * 
            from t1 join t2
               on t1.a = t2.c
        ) sample (50);

两种方法都有效,但返回的记录数是 57%,而不是两种情况下的 50%。

QUALIFY ROW_NUMBER() OVER (ORDER BY RANDOM()) 是更好的选择吗?虽然这确实适用于 WHERE 子句,但我不知道如何设置百分比而不是最大行数。示例:

SELECT * FROM MyTable
WHERE country = ‘USA’
AND load_date = CURRENT_DATE
QUALIFY ROW_NUMBER() OVER (ORDER BY RANDOM()) = (50)

--这给了我 50 行,而不是 50% 的行或 4,457 行(本示例中 where 子句后的总行数为 8,914)

【问题讨论】:

【参考方案1】:

在执行 where 子句之前,您需要先对表进行抽样。我相信在您的示例中,首先运行 where 子句,然后对其进行采样。试试这个(未经测试):

with ct as (
   SELECT * FROM MyTable SAMPLE (50)
)
select 
   *
from ct 
WHERE country = ‘USA’ 
AND load_date = CURRENT_DATE

或者我想这个:

select 
   *
from (SELECT * FROM MyTable SAMPLE (50))
WHERE country = ‘USA’ 
AND load_date = CURRENT_DATE

【讨论】:

正确,我只需要按百分比拆分表中的某些行。不幸的是,上述方法不起作用,因为随机样本必须是表的子集(USA 和 CURRENT_DATE),而不是相反。【参考方案2】:

您可以使用percent_rank() 代替row_number()

SELECT * FROM MyTable
WHERE country = 'USA'
AND load_date = CURRENT_DATE
QUALIFY PERCENT_RANK() OVER (ORDER BY RANDOM()) <= 0.5

【讨论】:

不幸的是,这不起作用。随机样本前的计数为 8,914。其中一半是 4,457。当我运行上述程序时,我仍然得到 4,457。知道为什么吗?所有这些变化都给了我 8,914;不是一半:QUALIFY PERCENT_RANK() OVER (ORDER BY RANDOM()) 【参考方案3】:

SAMPLE(50) 不是准确返回表中 50% 行的功能。这更像是“生成每行的随机数并评估该数字低于或高于百分比”。所以,它不会产生确定性的结果,而且会因为随机性而产生一定的偏差。

SAMPLE / TABLESAMPLE — 雪花文档: https://docs.snowflake.com/en/sql-reference/constructs/sample.html

BERNOULLI(或 ROW):包括概率为 p/100 的每一行。类似于为每一行翻转一个加权硬币。

如果您想以 50/50 的比例将表拆分为 2 个数据集,NTILE() 会很有帮助。

NTILE(n) 是一个函数,通过为每一行顺序和循环地生成 1 到 n 个数字,将有序数据集平均划分为参数中指定的“桶”数。例如NTILE(2) OVER (ORDER BY C1)C1列排序的每一行依次生成1、2、1、2、...,因此您可以使用“BUCKET”列中的值拆分数据集。

NTILE — 雪花文档: https://docs.snowflake.com/en/sql-reference/functions/ntile.html

将有序数据集平均划分为由 constant_value 指定的桶数。桶按从 1 到 constant_value 的顺序编号。

因此,如果您想从一个表中随机抽取 50% 的行,您可以使用 ORDER BY RANDOM()NTILE() 函数,如下所示:

with ntiled as (
    select *, ntile(2) over (order by random()) bucket
    from snowflake_sample_data.tpch_sf1.customer
)
select count_if(bucket = 1), count_if(bucket = 2)
from ntiled
;
/*
COUNT_IF(BUCKET = 1)    COUNT_IF(BUCKET = 2)
75000   75000
*/

【讨论】:

以上是关于从 Snowflake 中的表中选择随机百分比(使用 WHERE 子句时)的主要内容,如果未能解决你的问题,请参考以下文章

将数据从现有视图加载到 Snowflake 中的表并安排它每天运行 - 增量

从表中随机选择一定百分比的条目

如何从 PostgreSQL 中的另一个表中更新具有随机 id 的表

雪花中的 JavaScript UDF

Snowflake 中对非常大的表进行删除操作的最佳方法是啥?

如何使用 Snowflake Javascript 存储过程或函数遍历表中的所有列?