使用随机唯一编号更新每行中的列

时间:2017-09-30 23:27:36

标签: mysql random sql-update

我有一张约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查询时遇到了问题。

编辑: 我没有上述随机性的问题,因为我没有将它用于任何安全性purpoces,只是为每个不仅增加的行创建唯一 ID。由于用于验证另一行中是否已存在相同数字的选择,我无法使用UPDATE,这就是问题所在。

6 个答案:

答案 0 :(得分:2)

对2M记录进行测试,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)

答案 1 :(得分:1)

这是一种简单的方法。我用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 |
+----+------------+

现在,这些数字被随机分配给行,但每行都有一个唯一值。

但是,在为后续插入的行分配值时,它不会是随机的。

答案 2 :(得分:0)

您可以使用更新触发器修改现有行,使用插入触发器为新行生成随机数。在触发器主体中,您将生成一个随机数并检查它是否已存在于表中。您可以循环执行此操作,并在找到新的(唯一)数字后立即离开循环。

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(平均)迭代次数。

答案 3 :(得分:0)

您可以生成前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_uid(在这里称为random_id)设置为rnd_id:

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

关于使用更新并使用同一表进行选择的麻烦,我认为窍门是为表使用不同的别名。请参阅MySql - Update table using select statment from same table

这解决了为所有表填充random_uid的问题。在我的情况下,当我要添加一行时,我只是添加一个random_uid,它等于表中元素的数量,所以为N(因为我从0开始)。就我而言,这足够好,但总的来说,取决于您的约束。

答案 4 :(得分:0)

从@BillKarwin借用,UUID提供了一种获取数据库中随机字段和唯一字段的方法,而Mysql8提供了部分但完全可行的支持。

要存储它们,您的字段必须为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();

答案 5 :(得分:-1)

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

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 ;

这实际上只是一种蛮力的方式,并且有各种各样的方法可以优化性能,但这可以完成工作,快速而肮脏。我真的建议这样做,例如与UUIDs。