mysql 用随机的唯一数字更新每行中的一列

zynd9foi  于 2023-06-21  发布在  Mysql
关注(0)|答案(7)|浏览(127)

我有一个大约20k行的小表。在该表中有一个名为random_uid(INT NOT NULL)的列。我想用一个随机的唯一数字更新所有20k行。
由于我的表很小,我认为不需要使用字符串或UUID,所以我使用了

SELECT FLOOR(RAND() * 100000000) AS random_num
FROM table1 
WHERE "random_num" NOT IN (SELECT random_uid FROM table1)
LIMIT 1;

我的问题是我不能从同一个表中更新和选择,所以我在创建UPDATE查询时遇到了麻烦。

  • 编辑:* 我对上面的随机性没有问题,因为我不是为了任何安全目的而使用它,只是为了为每一行创建 * 唯一的 * id,而不仅仅是递增。因为我使用select来验证另一行中不存在相同的数字,所以我不能使用UPDATE,这就是问题所在。
smdncfj3

smdncfj31#

对200万条记录进行测试,100次迭代。测试成功。

UPDATE IGNORE table1 SET random_uid = ( RAND( ) * ( SELECT countID
FROM (
SELECT MAX(random_uid) + COUNT(1) + 1 countID
FROM table1) AS t3)
) + ( 
SELECT maxID
FROM (SELECT MAX( random_uid ) maxID FROM table1) AS t)
mrzz3bfm

mrzz3bfm2#

可以使用update触发器修改现有行,使用insert触发器为新行生成随机数。在触发器主体中,生成一个随机数,并检查它是否已经存在于表中。你可以在一个循环中完成它,一旦找到一个新的(唯一的)数字,就离开这个循环。

UPDATE触发器

DELIMITER //
create trigger table1_before_update
before update on table1 for each row 
begin
    declare rnd_num integer;
    loop1: loop
        set rnd_num := floor(rand() * 100000000);
        if not exists (select * from table1 where random_num = rnd_num) then
            set new.random_num = rnd_num;
            leave loop1;
        end if;
    end loop;
end//
DELIMITER ;

您可以使用以下命令更新表中的所有行:

update table1 set random_num = null where 1 = 1;

注意,列random_num必须是可空的。但它可以是UNIQUE。所以你可以把它定义为random_num int null unique
由于您只需要执行此步骤一次,因此现在可以放下该触发器。

INSERT触发器

DELIMITER //
create trigger table1_before_insert
before insert on table1 for each row
begin
    declare rnd_num integer;
    loop1: loop
        set rnd_num := floor(rand() * 100000000);
        if not exists (select * from table1 where random_num = rnd_num) then
            set new.random_num = rnd_num;
            leave loop1;
        end if;
    end loop;
end//
DELIMITER ;

INSERT触发器具有相同的主体。插入新行时,不需要设置random_num列。触发器会搞定的它甚至可以很好地使用批量插入:

insert into table1 (data) values
    ('data1'),
    ('data2'),
    ('data3'),
    ('data4'),
    ('data5');

演示:http://rextester.com/ZIDG57947
请注意,我在演示中使用FLOOR(RAND() * 10)来演示小范围内的唯一性。但是-您不应该尝试插入比可能的唯一数字的数量更多的行:-)
对于20K行和100M个可能的唯一数字,循环将需要每行1.0002次(平均)迭代。

vwhgwdsa

vwhgwdsa3#

更新2023-06-08

如果你对UUID满意,你可以在mysql 8上轻松地完成这一点:

SET field = (UUID())
where id=23;

相应地调整where子句以定位要更新的行。我提供id=23只是为了最大限度地减少有人通过复制粘贴无意中更新数据库中所有行的风险。

原创帖子

借用@BillKarwin,UUID提供了一种在数据库中获取随机和唯一字段的方法,Mysql 8有一些部分但完全可行的支持。
要存储它们,您的字段需要是VARCHAR(37)。确保您也已为该字段指定了唯一约束。有更有效的方法来存储它们,将它们打包成16字节大小的二进制文件,但这超出了本文的范围-网上有其他文章解释如何打包uuid。

SET @i = UUID(); 
UPDATE table1 set uuid_field = @i := UUID(); 
SELECT * from table1 LIMIT 10;

在上述查询之后,每个uuid_field都有一个通用的唯一id。您还需要有一个触发器,在插入时填充列的值。假设字段名为'uuid_field'并且是一个varchar,下面是触发器:

CREATE TRIGGER trigger_name
BEFORE INSERT ON table1
FOR EACH ROW
SET new.uuid_field = UUID();
rfbsl7qr

rfbsl7qr4#

您可以生成前N个整数的随机序列,并使用该序列更新表(其中N是表中的行数)。

Update table1 as st join (Select id, rnd_id
from (Select @rn3:=@rn3+1 as rowid, id from (select @rn3:=-1) as t4 cross join table1) as t5
join 
(Select @rn2:=@rn2+1 as rowid, rnd_id from (SELECT @rn2:=-1) as t1 cross join
(Select @rn1:=@rn1+1 as rnd_id from (SELECT @rn1:=-1) as t3 cross join table1 order by Rand()) as t2) as t6
on t5.rowid=t6.rowid) as t7 on st.id=t7.id set st.random_id=t7.rnd_id;

说明:

(Select @rn1:=@rn1+1 as rnd_id from (SELECT @rn1:=-1) as t3 cross join table1 order by Rand()) as t2

构建N个数字的随机序列。我们使用一个变量,它对每一行都递增。(SELECT @rn1:=-1) as t3 cross join等价于set @rn1:=-1;我们使用交叉连接技巧将两个语句放在一行中。因此,这生成了从0到N-1的序列,并使用order by Rand()对其进行加扰
我们用一个行号来增加这个表

(Select @rn2:=@rn2+1 as rowid, rnd_id from (SELECT @rn2:=-1) as t1 cross join ...

我们以类似的方式用行号增加原始表:

(Select @rn3:=@rn3+1 as rowid, id from (select @rn3:=-1) as t4 cross join table1) as t5

我们使用行号连接这两个部分:

on t5.rowid=t6.rowid

我们有效地构建了一个表,其中一列包含id,另一列包含问题中提到的random_uid(称为rnd_id)。此时,我们可以继续更新,用新的rnd_id表扩充表,并将原始表(这里称为random_id)中的random_uid设置为等于rnd_id:

Update table1 as st join ... as t7 on st.id=t7.id set st.random_id=t7.rnd_id;

关于在同一个表中使用update和select的麻烦,我认为诀窍是为表使用不同的别名。参考MySql - Update table using select statment from same table
这解决了为所有表填充random_uid的问题。在我的例子中,当我添加一行时,我只是添加一个random_uid,它等于表中元素的数量,所以N(因为我从0开始)。这在我的情况下已经足够好了,但一般情况下并不是这样,这取决于您的限制。

nhaq1z21

nhaq1z215#

如果你有一个唯一的ID,你可以这样做:

UPDATE table SET field = CONCAT(MD5(CONCAT(id, NOW())), '-', id);

首先,根据id的MD5字符串和当前完整的日期时间创建一个随机值。由于MD5不是无限的,因此您可以从两个不同的字符串获得相同的散列;您可以通过将原始id连接到MD5字符串来解决它,因为该id是唯一的。

3htmauhk

3htmauhk6#

可能最简单的方法是重复运行这个查询:

UPDATE table1
SET random_uid = FLOOR(RAND() * 100000000);

在每轮之间,您可以调用:

SELECT random_uid, COUNT(*) FROM table1 GROUP BY random_uid HAVING COUNT(*) > 1

看看有没有重复的
如果你在MySQL Workbench中工作,你可以创建一个临时过程来为你做这件事,就像这样:

DELIMITER ;;
DROP PROCEDURE IF EXISTS __SET_UNIQUE_IDS__;;
CREATE PROCEDURE __SET_UNIQUE_IDS__()
BEGIN
    while_loop: WHILE 1 = 1 DO
        UPDATE table1 SET random_uid = FLOOR(RAND() * 100000000);
        IF NOT EXISTS (SELECT random_uid FROM table1 GROUP BY random_uid HAVING COUNT(*) > 1) THEN
            LEAVE while_loop;
        END IF;
    END WHILE;
END
;;
CALL __SET_UNIQUE_IDS__();;
DROP PROCEDURE __SET_UNIQUE_IDS__;;
DELIMITER ;

这实际上只是一种蛮力的方法,有各种各样的方法可以优化性能,但这可以快速完成工作。我真的会建议用另一种方式来做这件事,例如。的UUID。

frebpwbc

frebpwbc7#

这里有一个简单的方法。我用512行填充了一个测试表,然后这样做:

mysql> set @i = 0;

mysql> update table1 set random_num = @i:=@i+1 order by rand();

mysql> select * from table1 limit 10;
+----+------------+
| id | random_num |
+----+------------+
|  1 |        345 |
|  2 |        108 |
|  3 |         18 |
|  4 |        247 |
|  6 |        202 |
|  7 |        275 |
|  8 |        289 |
|  9 |        121 |
| 13 |        237 |
| 14 |        344 |
+----+------------+

这些数字现在随机分配给各行,但每行都有一个唯一的值。
但是,在为随后插入的行赋值时,它不会是随机的。

相关问题