ためすう

keep_if を使ってみる (Ruby)

2020-12-05

やったこと

keep_if を使ってみます。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

調査

$ irb
irb(main):001:0> a = %w[a b c d]
irb(main):002:0> a.keep_if {|v| v == 'a' || v == 'd'}
=> ["a", "d"]

block のなかで false 判定の要素を削除します。

ちなみに Array#select! Array#filter! でも同じことが出来ます。

irb(main):004:0> a = %w[a b c d]
irb(main):005:0> a.select! {|v| v == 'a' || v == 'd'}
=> ["a", "d"]
irb(main):006:0> a
=> ["a", "d"]
...
irb(main):009:0> a = %w[a b c d]
irb(main):010:0> a.filter! {|v| v == 'a' || v == 'd'}
=> ["a", "d"]
irb(main):011:0> a
=> ["a", "d"]

少し深ぼってみます

  # Equivalent to Set#keep_if, but returns nil if no changes were
  # made. Returns an enumerator if no block is given.
  def select!(&block)
    block or return enum_for(__method__) { size }
    n = size
    keep_if(&block)
    self if size != n
  end

  # Equivalent to Set#select!
  alias filter! select!

削除対象があるかどうかで、違いがあるようですね。

irb(main):012:0> a = %w[a b c d]
irb(main):013:0> a.filter! {|v| true }
=> nil
irb(main):014:0> a = %w[a b c d]
irb(main):015:0> a.keep_if {|v| true }
=> ["a", "b", "c", "d"]

コメントにある通り、削除対象がない場合に nil を返すのか self を返すのかで挙動が違うことが分かりました。

参考

SHOW PROFILE を使ってみる (MySQL)

2020-11-23

やったこと

SHOW PROFILE を使ってみます。

確認環境

$ mysql --version
mysql  Ver 14.14 Distrib 5.6.25, for Linux (x86_64) using  EditLine wrapper

調査

プロファイリング対象のクエリを発行

mysql> show variables like "%profiling%"
    -> ;
+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| have_profiling         | YES   |
| profiling              | OFF   |
| profiling_history_size | 15    |
+------------------------+-------+
3 rows in set (0.00 sec)
mysql> SELECT COUNT(*), created_at FROM bulk_test_org
    -> GROUP BY created_at
    -> ORDER BY COUNT(*) DESC
    -> LIMIT 10;
+----------+---------------------+
| COUNT(*) | created_at          |
+----------+---------------------+
|  8388608 | 2020-11-03 07:36:31 |
|  4194304 | 2020-11-03 07:34:32 |
|  2097152 | 2020-11-03 07:34:19 |
|  1048576 | 2020-11-03 07:34:12 |
|   524288 | 2020-11-03 07:34:07 |
|   262144 | 2020-11-03 07:34:03 |
|   131072 | 2020-11-03 07:34:01 |
|    65536 | 2020-11-03 07:34:00 |
|    32768 | 2020-11-03 07:33:59 |
|    16384 | 2020-11-03 07:33:58 |
+----------+---------------------+
10 rows in set (20.51 sec)

結果確認

mysql> SHOW PROFILES;
+----------+-------------+-------------------------------------------------------------------------------------------------------+
| Query_ID | Duration    | Query                                                                                                 |
+----------+-------------+-------------------------------------------------------------------------------------------------------+
|        1 |  0.00901450 | select count(*), created_at  from bulk_test group by created_at order by count(*) desc limit 3        |
|        2 |  0.00022100 | select * from bulk_test limit 3                                                                       |
|        3 |  0.00224100 | select * from bulk_test_org limit 3                                                                   |
|        4 |  0.00024325 | select * from bulk_test_org limit 3                                                                   |
|        5 |  0.00000075 | SELECT COUNT(*), created_at FROM bulk_test
GROUP BY created_at
ORDER BY COUNT(*) DESC
LIMIT 10     |
|        6 | 20.49806075 | SELECT COUNT(*), created_at FROM bulk_test_org
GROUP BY created_at
ORDER BY COUNT(*) DESC
LIMIT 10 |
+----------+-------------+-------------------------------------------------------------------------------------------------------+
6 rows in set, 1 warning (0.00 sec)

mysql> SHOW PROFILE;
+----------------------+-----------+
| Status               | Duration  |
+----------------------+-----------+
| starting             |  0.000060 |
| checking permissions |  0.000008 |
| Opening tables       |  0.000018 |
| init                 |  0.000020 |
| System lock          |  0.000009 |
| optimizing           |  0.000006 |
| statistics           |  0.000014 |
| preparing            |  0.000011 |
| Creating tmp table   |  0.000030 |
| Sorting result       |  0.000005 |
| executing            |  0.000003 |
| Sending data         | 20.480386 |
| Creating sort index  |  0.009607 |
| end                  |  0.000435 |
| removing tmp table   |  0.000028 |
| end                  |  0.000234 |
| query end            |  0.000015 |
| closing tables       |  0.001094 |
| freeing items        |  0.002723 |
| logging slow query   |  0.000010 |
| cleaning up          |  0.003346 |
+----------------------+-----------+
21 rows in set, 1 warning (0.01 sec)

参考

Amazon RDS から CSV を出力する方法 (MySQL)

2020-11-15

やったこと

Amazon RDS から CSV ファイルを出力します。

確認環境

※ RDS ではありませんが、コマンド実行元にCSVファイルを出力することで、 RDS でも同様に適用できます。

$ mysql --version
mysql  Ver 14.14 Distrib 5.6.25, for Linux (x86_64) using  EditLine wrapper

$ python --version
Python 2.6.6

調査

dump 対象となるテーブル

  • データベース名: test
  • テーブル名: test_table
mysql> select * from test_table;
+----+------+------+------+
| id | col1 | col2 | col3 |
+----+------+------+------+
|  1 | 2    | 3    | 4    |
|  2 | 2    | 3    | 4    |
+----+------+------+------+
2 rows in set (0.00 sec)

mysqldump から CSV を生成するツールの準備

mysqldump の出力を CSV に変換するツールを使います。

git clone https://github.com/jamesmishra/mysqldump-to-csv.git

clone したファイルが下記のように配置します。

$ pwd
/home/vagrant/rds-mysql

$ chmod 755 mysqldump_to_csv.py

mysqldump から CSV を取得する

$ mysqldump -h127.0.0.1 -uroot -p test test_table -t --single-transaction | mysqldump_to_csv.py
Enter password:
1,2,3,4
2,2,3,4

標準出力で CSV データを得ることができました。

参考

Rails の cache ストレージで Active Record のオブジェクトを入れると事故りやすいと思う件

2020-11-14

やったこと

今回、Rails のキャッシュ機構で、Active Record のオブジェクトを保存すると “色々、大変なことがあるぞ” ということについて書いていきたいと思います。

ちなみにここでいうキャッシュ機構は、cache_store のことを指します。

それでは始めます。

確認環境

$ bundle exec rails --version
Rails 5.2.4.3

$ mysql --version
mysql  Ver 14.14 Distrib 5.6.43, for osx10.13 (x86_64) using  EditLine wrapper

検証

準備

テーブル作成

CREATE TABLE `pencils` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `maker_name` varchar(50) NOT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC

データ登録

INSERT INTO pencils VALUES (NULL, 'maker1', NOW(), NOW());

app/models/pencil.rb

class Pencil < ApplicationRecord
end

config/environments/development.rb (抜粋)

config.cache_store = :memory_store

cache に保存する

$ bundle exec rails c
Running via Spring preloader in process 65049
Loading development environment (Rails 5.2.4.3)
irb(main):001:0> p = Pencil.last
   (1.3ms)  SET NAMES utf8mb4,  @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'),  @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
  Pencil Load (0.9ms)  SELECT  `pencils`.* FROM `pencils` ORDER BY `pencils`.`id` DESC LIMIT 1
=> #<Pencil id: 1, maker_name: "maker1", created_at: "2020-11-08 22:45:49", updated_at: "2020-11-08 22:45:49">
irb(main):002:0> Rails.cache.write('pencil1', p)
=> true
irb(main):003:0> Rails.cache.read('pencil1')
=> #<Pencil id: 1, maker_name: "maker1", created_at: "2020-11-08 22:45:49", updated_at: "2020-11-08 22:45:49">
irb(main):004:0> Rails.cache.read('pencil2')
=> nil

クラス名を変更する (そろそろ来るぞ!)

app/models/pencil.rb を下記のように変更します。

app/models/pencil2.rb

class Pencil2 < ApplicationRecord
end

保存してある cache を読み込む

irb(main):004:0> reload!
Reloading...
=> true
irb(main):005:0> Rails.cache.read('pencil1')
Traceback (most recent call last):
        1: from (irb):5
NameError (uninitialized constant Pencil)

はい。Pencil クラスがなくなってしまったので、ロードで失敗してしまいました。

これは、”色々、大変なことがあるぞ” の1つです。

対応方針

cacheストレージ使うときは、”安心なデータ” を入れる必要があります。

データ構造じゃない、value を入れるのが安心です。

例えば以下が “安心なデータ” と考えます。

  • 数値 例: 123
  • 文字列 例: あいう

配列、json、ハッシュもデータ構造が変わるときに対応を忘れないようにすれば、使って良いと思います。

Active Record のオブジェクトは、cacheストレージに保存しない方が安心です。

なぜかといえば、例えば、Railsのバージョンが変わったとき、ApplicationRecord の内部の構造も変わり

読み込めなくなる可能性があるからです。

はい。実際にありました。

ApplicationRecord は rails の本体に入っているため、この事態に気が付きにくいです。

また、システムが大きくなればなるほど、対応範囲の調査が大変になります(経験談)

まとめ

cacheストレージ使うときは、構造が変更される可能性が低いデータを入れましょう!!!

“安心なデータ” は、構造が変更される可能性が低いデータになります。

  • 数値 例: 123
  • 文字列 例: あいう

もちろん、配列、json、ハッシュなどを使ったほうがいいケースもあり、その場合は構造を変更するときに注意してください。

構造が変更される可能性があって、検知しづらいのが Active Record のオブジェクト だと思います。

Active Record のオブジェクト をcacheストレージに入れた方が良い場合があるかもしれませんが、

いまのところ、私は、Active Record のオブジェクト をcacheストレージに使わないほうが良いと思っています。

LOAD DATA INFILE を使う (MySQL)

2020-11-08

やったこと

データ登録するときに、外部ファイルを読み込む LOAD DATA INFILE を使ってみます。

確認環境

$ mysql --version
mysql  Ver 14.14 Distrib 5.6.25, for Linux (x86_64) using  EditLine wrapper

調査

対象テーブル

移行前テーブル

mysql> select count(id) from bulk_test
    -> ;
+-----------+
| count(id) |
+-----------+
|  16777216 |
+-----------+
1 row in set (6.41 sec)

テーブル名は、bulk_test_1 bulk_test_2 とインクリメントします。

CREATE TABLE `bulk_test_1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `text_data` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
  `uniq_col` int(11) DEFAULT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniq_col` (`uniq_col`)
) ENGINE=InnoDB AUTO_INCREMENT=17366761 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
;

insert select で投入

mysql> INSERT INTO bulk_test_2 SELECT * FROM bulk_test;
Query OK, 16777216 rows affected (1 min 33.62 sec)
Records: 16777216  Duplicates: 0  Warnings: 0
mysql> SET FOREIGN_KEY_CHECKS = 0;
Query OK, 0 rows affected (0.05 sec)

mysql> SET UNIQUE_CHECKS = 0;
Query OK, 0 rows affected (0.00 sec)

mysql> SET AUTOCOMMIT = 0;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO bulk_test_1 SELECT * FROM bulk_test;
Query OK, 16777216 rows affected (1 min 49.48 sec)
Records: 16777216  Duplicates: 0  Warnings: 0

LOAD DATA LOCAL INFILE

投入する CSV 例

1,dummy text34,1,2020-11-03 07:31:48,2020-11-03 07:31:48
2,dummy text58,2,2020-11-03 07:33:16,2020-11-03 07:33:16
[vagrant@localhost ~]$ time mysql -u root -p test -e "LOAD DATA LOCAL INFILE '/tmp/bulk_test3.csv' INTO TABLE bulk_test_4 FIELDS TERMINATED BY ','"
Enter password:

real	1m40.355s
user	0m0.067s
sys	0m2.468s

LOAD DATA INFILE

投入する CSV 例

1,dummy text34,1,2020-11-03 07:31:48,2020-11-03 07:31:48
2,dummy text58,2,2020-11-03 07:33:16,2020-11-03 07:33:16
mysql> LOAD DATA INFILE '/tmp/bulk_test3.csv'
    ->   INTO TABLE bulk_test_3
    ->   FIELDS TERMINATED BY ','
    -> ;
Query OK, 16777216 rows affected (1 min 34.17 sec)
Records: 16777216  Deleted: 0  Skipped: 0  Warnings: 0

mysqldump で dump を流し込む

[vagrant@localhost ~]$ time mysqldump -uroot -p test bulk_test > /tmp/20201108.sql
Enter password:

real	0m47.587s
user	0m13.386s
sys	0m2.482s

投入前に別名に rename しておきます。

mysql> rename table bulk_test to bulk_test_org
    -> ;
Query OK, 0 rows affected (0.02 sec)
[vagrant@localhost ~]$ time mysql -u root -p test < /tmp/20201108.sql
Enter password:

real	2m39.268s
user	0m14.920s
sys	0m2.572s

まとめ

自分の PC で検証した限りだと、INSERT INTO ... SELECTLOAD DATA INFILE は、そんなに速度差はないみたいでした。

CSV 出力の時間が追加されるので、合計では INSERT INTO ... SELECT の方が短時間で出来るかもしれません。

参考

MySQL で CSV 出力する (MySQL)

2020-11-08

やったこと

MySQL のデータを CSV 出力する方法を2通りやってみます。

確認環境

$ mysql --version
mysql  Ver 14.14 Distrib 5.6.25, for Linux (x86_64) using  EditLine wrapper

調査

mysqldump で CSV を出力する

mysqldump -uroot -p --tab=/tmp --fields-terminated-by=, test bulk_test
[vagrant@localhost ~]$ ls -lh /tmp/
合計 985M
-rw-rw-r--  1 vagrant vagrant 1.6K 11月  7 11:15 2020 bulk_test.sql
-rw-rw-rw-  1 mysql   mysql   981M 11月  7 11:15 2020 bulk_test.txt

mysqldump --help より

-T, –tab=name Create tab-separated textfile for each table to given path. (Create .sql and .txt files.) NOTE: This only works if mysqldump is run on the same machine as the mysqld server.

–fields-escaped-by=name Fields in the output file are escaped by the given character.

mysql で CSV を出力する

mysql> SELECT * FROM bulk_test
    ->     INTO OUTFILE '/tmp/bulk_test2.csv'
    ->   FIELDS TERMINATED BY ','
    -> ENCLOSED BY '"'
    ->  ESCAPED BY '"'
    ->    LINES TERMINATED BY '\r\n';
Query OK, 16777216 rows affected (19.63 sec)
[vagrant@localhost ~]$ ls -lh /tmp/ | grep test2
-rw-rw-rw-  1 mysql   mysql   1.1G 11月  7 11:19 2020 bulk_test2.csv

大量データを作成する (MySQL)

2020-11-07

やったこと

テストデータで、1000万件のデータを用意する必要があったので、

MySQL だけで完結できるようにやってみます。

確認環境

$ mysql --version
mysql  Ver 14.14 Distrib 5.6.25, for Linux (x86_64) using  EditLine wrapper

調査

テーブル準備

CREATE TABLE IF NOT EXISTS `bulk_test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`text_data` varchar(20) NOT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
;

INSERT 実行

1件目

INSERT INTO bulk_test values (null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW());

これを欲しい件数まで繰り返します。

INSERT bulk_test (id, text_data, created_at, updated_at)
  SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;

1 -> 2 -> 4 -> 8 … とどんどん投入データが増えていきます。

実行結果

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)
    ->   SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 1 row affected (0.01 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 8 rows affected (0.00 sec)
Records: 8  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 16 rows affected (0.00 sec)
Records: 16  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 32 rows affected (0.00 sec)
Records: 32  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 64 rows affected (0.00 sec)
Records: 64  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 128 rows affected (0.00 sec)
Records: 128  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 256 rows affected (0.02 sec)
Records: 256  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 512 rows affected (0.00 sec)
Records: 512  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 1024 rows affected (0.01 sec)
Records: 1024  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 2048 rows affected (0.01 sec)
Records: 2048  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 4096 rows affected (0.03 sec)
Records: 4096  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 8192 rows affected (0.10 sec)
Records: 8192  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 16384 rows affected (0.06 sec)
Records: 16384  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 32768 rows affected (0.12 sec)
Records: 32768  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 65536 rows affected (0.22 sec)
Records: 65536  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 131072 rows affected (0.45 sec)
Records: 131072  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 262144 rows affected (1.09 sec)
Records: 262144  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 524288 rows affected (2.05 sec)
Records: 524288  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 1048576 rows affected (4.62 sec)
Records: 1048576  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 2097152 rows affected (9.64 sec)
Records: 2097152  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 4194304 rows affected (22.91 sec)
Records: 4194304  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 8388608 rows affected (44.87 sec)
Records: 8388608  Duplicates: 0  Warnings: 0

mysql> select count(*) from bulk_test;
+----------+
| count(*) |
+----------+
| 16777216 |
+----------+
1 row in set (6.51 sec)

16777216 件のレコードが出来上がりました。

unique キー制約、外部キー制約があると、もう少し複雑になるので、

ruby, python などでプログラム書いた方が速いかもしれません。

MySQL でランダムな数値を出力する (MySQL)

2020-11-03

やったこと

MySQL で、テストデータを作成するときに、ランダムな数値を出力したいことがあったので

やってみます。

確認環境

$ mysql --version
mysql  Ver 14.14 Distrib 5.6.25, for Linux (x86_64) using  EditLine wrapper

調査

例えば、0 ~ 5 までの数字を出力するとします。

  • RAND (0 <= v < 1.0) を返す
  • ROUND(X) 四捨五入する
  • FLOOR(X) X 以下で最大の整数値を返す

これを組み合わせ使います。

rand の動きチェック

mysql> SELECT RAND();
+---------------------+
| RAND()              |
+---------------------+
| 0.28683608517687403 |
+---------------------+
1 row in set (0.00 sec)

mysql> SELECT RAND();
+--------------------+
| RAND()             |
+--------------------+
| 0.9208513711773337 |
+--------------------+
1 row in set (0.00 sec)

round の動きチェック

mysql> SELECT ROUND(2.4);
+------------+
| ROUND(2.4) |
+------------+
|          2 |
+------------+
1 row in set (0.00 sec)

mysql> SELECT ROUND(2.5);
+------------+
| ROUND(2.5) |
+------------+
|          3 |
+------------+
1 row in set (0.00 sec)

mysql> SELECT ROUND(2.47);
+-------------+
| ROUND(2.47) |
+-------------+
|           2 |
+-------------+
1 row in set (0.00 sec)

floor の動きチェック

mysql> SELECT FLOOR(2.4);
+------------+
| FLOOR(2.4) |
+------------+
|          2 |
+------------+
1 row in set (0.00 sec)

mysql> SELECT FLOOR(3.2);
+------------+
| FLOOR(3.2) |
+------------+
|          3 |
+------------+
1 row in set (0.00 sec)

mysql> SELECT FLOOR(3.5);
+------------+
| FLOOR(3.5) |
+------------+
|          3 |
+------------+
1 row in set (0.00 sec)

0 ~ 5 までの数字を出力する (ROUND + RAND)

mysql> SELECT ROUND(RAND() * 5);
+-------------------+
| ROUND(RAND() * 5) |
+-------------------+
|                 1 |
+-------------------+
1 row in set (0.00 sec)

mysql> SELECT ROUND(RAND() * 5);
+-------------------+
| ROUND(RAND() * 5) |
+-------------------+
|                 5 |
+-------------------+
1 row in set (0.00 sec)

これだと

  • 0.0 ~ 0.4 => 0
  • 0.5 ~ 1.4 => 1

となり、端っこの方でばらつくかもしれません。

なので rand() x 6 して、0... ~ 5.9... して切り捨てすればいい感じになりそうです。

0 ~ 5 までの数字を出力する (FLOOR + RAND)

mysql> SELECT FLOOR(RAND() * 6);
+-------------------+
| FLOOR(RAND() * 6) |
+-------------------+
|                 4 |
+-------------------+
1 row in set (0.00 sec)

mysql> SELECT FLOOR(RAND() * 6);
+-------------------+
| FLOOR(RAND() * 6) |
+-------------------+
|                 0 |
+-------------------+
1 row in set (0.00 sec)

参考

mysqldump を使ってみる (MySQL)

2020-11-01

やったこと

mysqldump を使ってみます。

確認環境

$ mysql --version
mysql  Ver 14.14 Distrib 5.6.25, for Linux (x86_64) using  EditLine wrapper
$ mysqldump --version
mysqldump  Ver 10.13 Distrib 5.6.25, for Linux (x86_64)

調査

dump 対象となるテーブル

  • データベース名: test
  • テーブル名: test_table
mysql> select * from test_table;
+----+------+------+------+
| id | col1 | col2 | col3 |
+----+------+------+------+
|  1 | 2    | 3    | 4    |
|  2 | 2    | 3    | 4    |
+----+------+------+------+
2 rows in set (0.00 sec)

mysqldump を実行する

$ mysqldump -h127.0.0.1 -uroot -p test test_table -t --single-transaction

出力結果

Enter password:
-- MySQL dump 10.13  Distrib 5.6.25, for Linux (x86_64)
--
-- Host: 127.0.0.1    Database: test
-- ------------------------------------------------------
-- Server version	5.6.25

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

--
-- Dumping data for table `test_table`
--

LOCK TABLES `test_table` WRITE;
/*!40000 ALTER TABLE `test_table` DISABLE KEYS */;
INSERT INTO `test_table` VALUES (1,'2','3','4'),(2,'2','3','4');
/*!40000 ALTER TABLE `test_table` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2020-11-01  9:07:35

mysqldump オプションの説明 (mysqldump –helpより抜粋)

-t で、データのみをdumpします。

  -t, --no-create-info
                      Don't write table creation info.

single-transaction

  --single-transaction
                      Creates a consistent snapshot by dumping all tables in a
                      single transaction.

実際に使う場合は、標準出力をファイルに書き出して、使うと思います。

dotenv を使ってみる (gem)

2020-10-31

やったこと

環境変数をファイルで管理する dotenv を使ってみます。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

$ rails --version
Rails 5.2.4.1

調査

install

Gemfile

gem 'dotenv-rails', groups: [:development, :test]
$ bundle install

RAILS_ENV=development用の環境変数をセット

.env.development

HOGE1='hoge1hoge1'
HOGE2='hoge2hoge2'

rails console

$ bundle exec rails c
Running via Spring preloader in process 47643
Loading development environment (Rails 5.2.4.3)
irb(main):001:0> ENV['HOGE1']
=> "hoge1hoge1"
irb(main):002:0> ENV['HOGE3']
=> nil
irb(main):003:0> ENV['HOGE2']
=> "hoge2hoge2"

RAILS_ENV=test用の環境変数をセット

.env.test

HOGE1='test hoge1hoge1'
HOGE2='test hoge2hoge2'

rails console

$ RAILS_ENV=test bundle exec rails c
Running via Spring preloader in process 48051
Loading test environment (Rails 5.2.4.3)
irb(main):001:0> ENV['HOGE1']
=> "test hoge1hoge1"
irb(main):002:0> ENV['HOGE3']
=> nil
irb(main):003:0> ENV['HOGE2']
=> "test hoge2hoge2"

注意!!!

現在、ドキュメントにあるこの gem はなくなってしまったみたいです。

gem 'gem-that-requires-env-variables'

参考