生成具有条件 Snowflake 的唯一随机数

Generating unique random number with condition Snowflake

提问人:Python_Hey 提问时间:9/13/2022 更新时间:9/14/2022 访问量:2484

问:

我想在 Snowflake 中生成一些具有特定起点/终点的唯一随机数。我希望这些数字从 1,000 开始,到 1,000,000 结束。

另一个要求是在这些数字的开头连接一个字符串。

到目前为止,我一直在使用这个语句:

SELECT CONCAT('TEST-' , uniform(10000, 99000, RANDOM()));

它按预期工作,并给了我例如“TEST-31633”的输出。

但是,问题是我正在为大量行生成这些行,并且我需要它们完全唯一。

我听说过“SEQ1”函数,但不确定如何指定起点以及在开头添加带有 CONCAT 函数的“TEST-”。理想情况下,它们不会严格排列,而是彼此不同。

谢谢

sql 随机 snowflake-cloud-data-platform 序列 DML

评论


答:

0赞 Himanshu Kandpal 9/14/2022 #1

您是否尝试过 UNIFORM,也许这对您有用。https://docs.snowflake.com/en/sql-reference/functions/uniform.html

select  CONCAT('TEST-' , uniform(10000, 99000, random())) from table(generator(rowcount => 100));

评论

0赞 Python_Hey 9/14/2022
是的,我在上面的查询中有 UNIFORM,不幸的是这仍然会产生重复项
1赞 Himanshu Kandpal 9/14/2022
您可以尝试的一种方法是在不同的范围内使用 UNIFORM 2 次并 CONCAT 字符串,这可能会给您带来独特的效果。您可以生成一个 row_numner() 并使用该数字附加到字符串中。docs.snowflake.com/en/sql-reference/functions/row_number.html
2赞 Gokhan Atil 9/14/2022 #2

除非您使用某种计算部分,否则无法保证随机数生成器函数的唯一性。例如:

create or replace table testX as
select  CONCAT('TEST-' , to_char(seq4(),'0000'),  
uniform(100000, 990000, random())) c 
from table(generator(rowcount => 1000));

评论

0赞 Python_Hey 9/14/2022
谢谢,这是完美的,我已经标记为答案。如果您有时间的话,还有一件事:这里的最终目标是在表的列中用唯一的 TEST-xxxx 替换一些数字。我可以将上述语句作为嵌套查询的一部分进行任何更改吗?我从来没有设法以这种方式使用发电机表。假设我需要表 A 中的 row1 和 row2,并想将这些随机数附加为第三列 FROM table(generator...或者甚至干脆使用查询来 UPDATE 表 Set Row 1 = ?
1赞 Gokhan Atil 9/14/2022
这应该有效: UPDATE tableName Set col3 = CONCAT('TEST-' , to_char(seq4(),'0000'), uniform(100000, 990000, random()));
1赞 Greg Pavlik 9/14/2022 #3

每当需要在范围内分配唯一的随机数时,最好通过随机排序而不是生成新的随机数来处理。只需生成序列,然后随机排列其顺序即可。

根据用例,您可以简单地生成行,并对所选数量进行限制:order by random()

select * from (
select 'TEST-' || (seq4() + 10000)::string as MY_TEST_ID
from table(generator(rowcount => 89000))
) order by random() limit 10000
;

这个问题的通用解决方案更复杂,但肯定是可能的。JavaScript UDTF 可以使用范围内的所有值填充数组并对其进行随机排序。在 JavaScript UDTF 上使用 window 函数来确保行分布在单个块中,它将允许在任何 SQL 语句的某个范围内创建唯一的随机整数。

首先,创建表函数:

create or replace function UNIQUE_RANDOM_INTEGERS(LBOUND float, UBOUND float)
returns table (UNIQUE_RAND_INT float)
language javascript
strict volatile
as
$$
{
    initialize: function (argumentInfo, context) {
        this.lBound = argumentInfo.LBOUND.constValue;
        this.uBound = argumentInfo.UBOUND.constValue;
        this.rSpace = this.uBound - this.lBound + 1;
        
        if (this.lBound >= this.uBound) throw new Error(">>>  LBOUND and UBOUND must be constants and UBOUND must be greater than LBOUND <<<");
        if (this.rSpace > 25000000) throw new Error (">>> The difference between LBOUND and UBOUND must be 25,000,000 or less.");
        
        this.rands = new Array(this.rSpace);
        this.currentRow = 0;
        for (let i = 0; i < this.rands.length; i++) {
            this.rands[i] = this.lBound + i;
        }
        this.rands = shuffle(this.rands);
        
        function shuffle(array) {
            let currentIndex = array.length,  randomIndex;
            while (currentIndex != 0) {
                randomIndex = Math.floor(Math.random() * currentIndex);
                currentIndex--;
                [array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]];
            }
            return array;
        }
    },
    processRow: function (row, rowWriter, context) {
        //
    },
    finalize: function (rowWriter, context) {
        for (let i = 0; i < this.rSpace; i++) {
            rowWriter.writeRow({UNIQUE_RAND_INT:this.rands[i]});
        }
    },
}
$$;

create or replace function UNIQUE_RANDOM_INTEGERS(LBOUND int, UBOUND int)
returns table(UNIQUE_RAND_INT int)
language sql
as
$$
    select UNIQUE_RAND_INT::int from table(UNIQUE_RANDOM_INTEGERS(LBOUND::float, UBOUND::float) over (partition by 1))
$$;

然后,您可以使用以下 SQL 在某个范围内生成唯一的随机数:

select 'TEST-' || UNIQUE_RAND_INT as MY_RAND_VAL
from table(unique_random_integers(10000, 99000));

评论

0赞 Simeon Pilgrim 9/14/2022
同意,如果它也没有在任意窗口中重复,它就不是“随机”的。否则,这就像说我想要 100 卷 1d6 并且没有重复。
1赞 Simeon Pilgrim 9/14/2022 #4

另一个洗牌答案:

一些设置:

set range_start = 1;
set range_end = 10;
set range_len = $range_end - $range_start;
set name_width = 6;
select 
    'row_' ||lpad(row_number()over(order by true),$name_width,0) as row_num
from table(generator(ROWCOUNT => $range_len));

给:

ROW_NUM
row_000001
row_000002
row_000003
row_000004
row_000005
row_000006
row_000007
row_000008
row_000009

所以没有重复,现在洗牌它们,只需给它们和随机值并按顺序排序:

select 
    'row_' ||lpad(row_number()over(order by true),$name_width,0) as row_num
    ,random() as rnd_order
from table(generator(ROWCOUNT => $range_len))
order by rnd_order;
ROW_NUM RND_ORDER
row_000002 -7,719,769,195,714,581,512
row_000007 -7,227,137,977,152,948,936
row_000004 -4,651,924,808,142,841,706
row_000005 -4,571,360,566,799,746,506
row_000003 -1,059,648,113,216,115,246
row_000008 1,056,363,703,661,911,623
row_000001 2,281,704,740,829,606,855
row_000006 5,648,204,845,936,012,521
row_000009 8,998,464,501,571,068,967

将设置更改为所需的 RELM:

set range_start = 1000;
set range_end = 1000000;
set range_len = $range_end - $range_start;

现在,如果您只想要 500K 答案而不是 9,999K,请在最后限制 500000。.