我有一张约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
,这就是问题所在。
答案 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)
您可以使用更新触发器修改现有行,使用插入触发器为新行生成随机数。在触发器主体中,您将生成一个随机数并检查它是否已存在于表中。您可以循环执行此操作,并在找到新的(唯一)数字后立即离开循环。
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
。
由于您只需要执行此步骤一次,现在可以删除该触发器。
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。