質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.44%
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

Q&A

解決済

2回答

1929閲覧

MySQLでランダムなn文字を入れたいのですが、良いコードはございませんでしょうか?

origa3

総合スコア22

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

0グッド

0クリップ

投稿2023/04/10 23:15

実現したいこと

MySQLでランダムな値をカラムに入れたいです。

前提

必須の条件
・ランダムな6文字
・文字種は「アルファベット大文字、小文字、0-9の数字」の3つ

もしできればな条件
・重複させたくない

発生している問題・エラーメッセージ

エラーはありません。しかしざっと見てみるお、なんとなくランダムにならないような印象があります。

該当のソースコード

以下です。実行すると、ランダムというか、似通った値になってしまいます。(012345などという値も入り、ランダム性が疑わしいです。)

SQL

1update tbl set rand_str = 2SUBSTRING('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', FLOOR(RAND() * 62) + 1, 6);

試したこと

以下試すと上記よりはランダムっぽくなりましたが、どうにも冗長な印象があり改善したいです。

SQL

1update tbl set rand_str = 2CONCAT( 3SUBSTRING('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', FLOOR(RAND() * 62) + 1, 1), 4SUBSTRING('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', FLOOR(RAND() * 62) + 1, 1), 5SUBSTRING('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', FLOOR(RAND() * 62) + 1, 1), 6SUBSTRING('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', FLOOR(RAND() * 62) + 1, 1), 7SUBSTRING('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', FLOOR(RAND() * 62) + 1, 1), 8SUBSTRING('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', FLOOR(RAND() * 62) + 1, 1) 9)

確認したこと

上述のように、できれば「重複させたくない」と思っております。

まぁこの条件は達成できなくとも、UPDATE後に重複をSELECTし、重複レコードに対し再度UPDATEすればいいと思っておりましたので、いったん「試したこと」の実行後に、以下で重複をSELECTしてみました。

SQL

1SELECT rand_str, COUNT(*) FROM tbl GROUP BY rand_str HAVING COUNT(*) > 1;

すると全レコード約6万件に対し、570件の重複がSELECTされ驚きました。

といいますのは、「アルファベット大文字、小文字、0-9の数字」では 56,800,235,584 通りになるはずなのに、6万件のうち570件も重複する可能性は低いはず。一体なぜ…?という驚きです。

このことから、「試したこと」ではランダム性がやや低いのか、とも疑っております。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2023/04/10 23:32

回答じゃないのでこちらで。 つ「 Function 」
退会済みユーザー

退会済みユーザー

2023/04/10 23:41

uuid は選択肢に入りませんか?
m.ts10806

2023/04/11 00:07

SQLだけでやりきらなくても良いのでは
Zuishin

2023/04/11 00:56

SQL でも頑張ればできるのかもしれませんが、汎用プログラミング言語でデータを作成してインポートするのが簡単だと思います。 56,800,235,584 は 64 ビットに収まるので、0 から 56,800,235,583 までの乱数を生成することは容易です。 生成した数値をハッシュセットなどに格納し、ハッシュセットの要素数が欲しい数になるまでそれを繰り返します。 それが終わったら、ハッシュセットから一つずつ取り出しながら、それを 0 から 9 までの数字とアルファベットの大文字小文字で表せる 62 進数文字列に変換します。 変換の仕方は「N 進数 変換」でググるとすぐに見つかります。 変換後の文字列が求める文字列になるので、これを CSV ファイルに出力し、データベースにインポートします。
sazi

2023/04/11 00:59

MySQLのパスワード生成機能では駄目なのですか?
origa3

2023/04/12 01:29 編集

みなさまアドバイスありがとうございます。 いろいろな方法があるようで、参考にさせていただきます。
Zuishin

2023/04/12 01:13

前提が書かれていない上に聞いても返事をしないためにみんなで当てずっぽうを書いてたまたま当ったのがあれば解決という質問が増えてきましたね。
Zuishin

2023/04/12 01:25

前提を消した。
guest

回答2

0

ベストアンサー

56,800,235,584通りを想定しているということは同じ文字が2度でてもよいという認識ですかね?(56,800,235,584=62の6乗)
まずファンクションを作って

SQL

1drop function if exists rnd; 2delimiter // 3create function rnd(n int) 4returns varchar(255) DETERMINISTIC 5begin 6if n>255 then 7set @a=null; 8else 9set @a=""; 10set @b=1; 11while @b<=n 12do 13set @a:=concat(@a,substring('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',ceil(rand() * 62),1)); 14set @b:=@b+1; 15end while; 16end if; 17return @a; 18end 19//

こんな感じで指定

SQL

1select rnd(6);

調整

ちょっとランダムの精度を上げてみました。
同じコードを次に引き渡していくとランダム性はあがるかも

SQL

1create table tbl (id int primary key auto_increment,val varchar(6)); 2 3drop procedure if exists set_blank; 4delimiter // 5create procedure set_blank(in n int) 6begin 7set @sql:=concat('insert into tbl(val) values(\'\')',repeat(',(\'\')',n-1)); 8prepare stmt from @sql; 9execute stmt; 10end 11// 12delimiter ; 13 14drop function if exists str_shuffle; 15delimiter // 16create function str_shuffle(t text) 17returns text 18begin 19set @txt=t; 20set @ret=''; 21set @pos=0; 22set @u=length(t); 23WHILE @u > 0 24do 25set @pos=1 + floor(rand() * @u); 26set @ret = concat(@ret,mid(@txt,@pos,1)); 27set @txt = concat(left(@txt,@pos-1),mid(@txt,@pos+1,@u)); 28set @u=@u-1; 29end while; 30return @ret; 31end 32// 33delimiter ;

SQL

1call set_blank(60000); 2set @a:='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; 3update tbl set val=substr(@a:=str_shuffle(@a),1,6);

それでもいくつかはだぶりますけど

SQL

1select val,count(*) as cnt from tbl group by val 2order by cnt desc

投稿2023/04/11 01:21

編集2023/04/11 10:03
yambejp

総合スコア115239

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sazi

2023/04/11 02:00 編集

一括処理を考慮して、NOT DETERMINISTIC の方が良くないですか?
yambejp

2023/04/11 02:09 編集

TO:saziさん 私も最初NOT DETERMINISTIC(デフォルト)にしようと思ったのですが、私の環境がわるいのかエラーがでるので仕方なくDETERMINISTICにしてあります、どうやら SET GLOBAL log_bin_trust_function_creators = 1; を設定しておけばNOT DETERMINISTICが指定できるようですね・・・
origa3

2023/04/11 04:53

すごい!そのような関数を作っておくと後からいくらでも呼べるのですね。 どうもありがとうございます。勉強になりました。 ただし、下記に記すように質問と同じ現象が起こりました。 現状 tbl には約6万件のレコードがあるので、それに対し UPDATE tbl SET rand_str = rnd(6); を実行し、 SELECT rand_str, COUNT(*) FROM tbl GROUP BY rand_str HAVING COUNT(*) > 1; を実行して重複を探してみますと、 やはり質問と同様に600件弱の重複が見つかりました。 56,800,235,584通りの組み合わせに対して6万回の試行をしても、重複する可能性は相当低いはずですが、なぜ600件もの重複が起こるのか…。このランダム性の低さは何に起因するのかがやはり疑問です。
yambejp

2023/04/11 10:07

ランダム処理はしょせん疑似処理ですからねぇ、一応文字列をシャッフルする方式にするとランダム性はあがると思います
退会済みユーザー

退会済みユーザー

2023/04/11 16:09

まあネタではあるが インデックスを求める計算式は (乱数x最大値) より (乱数x係数+乱数x桁番号)% 最大値 のような余で算出する方がすきw
origa3

2023/04/12 00:56

ご調整実行させて頂きました。ほぼ(1, 2件しか)ダブらなくなります! 「重複レコードに対し再度UPDATEすればいい」とは申したものの内心面倒に感じていたところでしたが、その手間が大きく減りました。どうもありがとうございます。
guest

0

「アルファベット大文字、小文字、0-9の数字」では 56,800,235,584 通りになるはずなのに、6万件のうち570件も重複する可能性は低いはず。一体なぜ…?という驚きです。

この部分についてですが、文字コードの照合順位がutf8_general_ciだったりすると、WHEREやGROUP BYにおいてアルファベットの大文字と小文字の区別がありません。

下記SQLで結果が変わったりしないでしょうか?

sql

1SELECT rand_str, COUNT(*) FROM tbl GROUP BY BINARY rand_str HAVING COUNT(*) > 1;

投稿2023/04/11 21:21

編集2023/04/11 21:23
Eggpan

総合スコア2925

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

origa3

2023/04/12 01:02

> WHEREやGROUP BYにおいてアルファベットの大文字と小文字の区別がありません。 アドバイスありがとうございます。確認方法に問題がありえたとは気が付きませんでした。 > 文字コードの照合順位がutf8_general_ciだったりすると こちらの確認はわかりませんでした。「SHOW COLLATION」とやらを実行するとずらっと出ますがどれがその照合順位なのか… > 下記SQLで結果が変わったりしないでしょうか? こちらは1件も変化なしでした。(ただしONLY_FULL_GROUP_BYがあるとエラーになるので次のようにONLY_FULL_GROUP_BYは解除しましたが。) SET sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY','')); SELECT rand_str, COUNT(*) FROM tbl GROUP BY BINARY rand_str HAVING COUNT(*) > 1;
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.44%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問