- 投稿日:2021-12-05T19:54:07+09:00
MySQLは定義の型サイズによって性能が異なるのか
本日は個人的に気になっていた事を調べてみました。 それはMySQLは定義の型サイズによって性能が異なるのか?です というのも会社でenum値のカラムの型にvarchar(255)が散見されるため、これを短くする事は有効なのか気になった次第です。 ちなみに、今回はデータ長ではなく定義の長さが性能に与える影響を調べました ストレージサイズについてはデータ長と型サイズで変化します https://dev.mysql.com/doc/refman/8.0/ja/storage-requirements.html 準備 まずは2つのテーブルを定義します CREATE TABLE `sample1` ( `id` int NOT NULL AUTO_INCREMENT, `icol` int NOT NULL, `v16col` varchar(16) NOT NULL, `v64col` varchar(64) NOT NULL, `v255col` varchar(255) NOT NULL, `v256col` varchar(256) NOT NULL, PRIMARY KEY (`id`), KEY `idx_icol` (`icol`), KEY `idx_v16col` (`v16col`), KEY `idx_v64col` (`v64col`), KEY `idx_v255col` (`v255col`), KEY `idx_v256col` (`v256col`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `sample2` ( `id` int NOT NULL AUTO_INCREMENT, `icol` int NOT NULL, `v16col` varchar(16) NOT NULL, `v64col` varchar(64) NOT NULL, `v255col` varchar(255) NOT NULL, `v256col` varchar(256) NOT NULL, PRIMARY KEY (`id`), KEY `idx_icol` (`icol`), KEY `idx_v16col` (`v16col`), KEY `idx_v64col` (`v64col`), KEY `idx_v255col` (`v255col`), KEY `idx_v256col` (`v256col`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 初期データとして10万件のデータを投入 /home/go/app/mysql # go run insert.go --table=sample1 table:sample1 /home/go/app/mysql # go run insert.go --table=sample2 table:sample2 /home/go/app/mysql # ソースコード /home/go/app/mysql # cat insert.go package main import ( "database/sql" "flag" "fmt" _ "github.com/go-sql-driver/mysql" ) func main() { f := flag.String("table", "sample1", "target table") flag.Parse() fmt.Printf("table:%s\n", *f) db, err := sql.Open("mysql", "root:mypass@(go.mysql.local:3307)/test") if err != nil { fmt.Println("db error.") panic(err) } defer db.Close() ins, err := db.Prepare(fmt.Sprintf("INSERT INTO %s(icol,v16col,v64col,v255col,v256col) VALUES(?,?,?,?,?)", *f)) if err != nil { fmt.Println(err) panic(err) } for i := 1; i < 100000; i++ { ins.Exec(i, i, i, i, i) } } indexのサイズ indexのサイズはint型とvarchar型で異なりますが、定義長は影響しないようです。 SELECT stat_value, index_name, (stat_value * @@innodb_page_size) / 1024 / 1024 AS size_mb FROM innodb_index_stats WHERE stat_name = 'size' AND index_name like "idx_%"; +------------+-------------+------------+ | stat_value | index_name | size_mb | +------------+-------------+------------+ | 97 | idx_icol | 1.51562500 | | 225 | idx_v16col | 3.51562500 | | 225 | idx_v255col | 3.51562500 | | 225 | idx_v256col | 3.51562500 | | 225 | idx_v64col | 3.51562500 | | 97 | idx_icol | 1.51562500 | | 225 | idx_v16col | 3.51562500 | | 225 | idx_v255col | 3.51562500 | | 225 | idx_v256col | 3.51562500 | | 225 | idx_v64col | 3.51562500 | +------------+-------------+------------+ 10 rows in set (0.03 sec) selectのWHEREキーとしての性能 selectのWHEREキーとしての性能を計測しました。 実行する度に振れ幅はありますがint型が多少早く、varchar型は定義長によっての大きな差異は感じませんでした 実行結果 /home/go/app/mysql # time go run select.go --col=icol col:icol real 0m 6.23s user 0m 0.87s sys 0m 1.97s /home/go/app/mysql # time go run select.go --col=v16col col:v16col real 0m 6.53s user 0m 0.75s sys 0m 2.04s /home/go/app/mysql # time go run select.go --col=v64col col:v64col real 0m 6.55s user 0m 0.81s sys 0m 1.98s /home/go/app/mysql # time go run select.go --col=v255col col:v255col real 0m 6.57s user 0m 0.76s sys 0m 2.04s /home/go/app/mysql # time go run select.go --col=v256col col:v256col real 0m 6.58s user 0m 0.85s sys 0m 1.92s ソースコード /home/go/app/mysql # cat select.go package main import ( "database/sql" "flag" "fmt" _ "github.com/go-sql-driver/mysql" "strconv" ) type Sample struct { Id int Icol int V16col string V64col string V255col string V256col string } func main() { f := flag.String("col", "icol", "col") flag.Parse() fmt.Printf("col:%s\n", *f) db, err := sql.Open("mysql", "root:mypass@(go.mysql.local:3307)/test") if err != nil { fmt.Println("db error.") panic(err) } defer db.Close() for i := 1; i < 10000; i++ { var sample = Sample{} err = db.QueryRow(fmt.Sprintf("SELECT * FROM sample1 WHERE %s = ?", *f), strconv.Itoa(i)).Scan(&sample.Id, &sample.Icol, &sample.V16col, &sample.V64col, &sample.V255col, &sample.V256col) if err != nil { fmt.Println("Query error.") panic(err) } } } selectの結合キーとしての性能 selectの結合キーとしての性能を計測しました。 こちらもint型が多少早く、varchar型は定義長によっての大きな差異は感じませんでした 実行結果 /home/go/app/mysql # time go run select-join.go --col=icol col:icol real 0m 7.69s user 0m 0.85s sys 0m 2.00s /home/go/app/mysql # time go run select-join.go --col=v16col col:v16col real 0m 8.15s user 0m 0.82s sys 0m 2.02s /home/go/app/mysql # time go run select-join.go --col=v64col col:v64col real 0m 8.20s user 0m 0.78s sys 0m 2.06s /home/go/app/mysql # time go run select-join.go --col=v255col col:v255col real 0m 8.34s user 0m 0.81s sys 0m 2.02s /home/go/app/mysql # time go run select-join.go --col=v256col col:v256col real 0m 8.32s user 0m 0.79s sys 0m 1.99s ソースコード /home/go/app/mysql # cat select-join.go package main import ( "database/sql" "flag" "fmt" _ "github.com/go-sql-driver/mysql" "strconv" ) type Sample struct { Id int Icol int V16col string V64col string V255col string V256col string } func main() { f := flag.String("col", "icol", "col") flag.Parse() fmt.Printf("col:%s\n", *f) db, err := sql.Open("mysql", "root:mypass@(go.mysql.local:3307)/test") if err != nil { fmt.Println("db error.") panic(err) } defer db.Close() for i := 1; i < 10000; i++ { var sample = Sample{} err = db.QueryRow(fmt.Sprintf("SELECT s2.* FROM sample1 s1 INNER JOIN sample2 s2 ON s1.%s = s2.%s WHERE s1.%s = ?", *f, *f, *f), strconv.Itoa(i)).Scan(&sample.Id, &sample.Icol, &sample.V16col, &sample.V64col, &sample.V255col, &sample.V256col) if err != nil { fmt.Println("Query error.") panic(err) } } } updateの対象のキーとしての性能 updateの更新対象キーとしての性能を計測しました。 定義での差異はあまり出ませんでした 実行結果 /home/go/app/mysql # time go run update.go --col=icol col:icol real 5m 5.44s user 0m 4.47s sys 0m 17.68s /home/go/app/mysql # time go run update.go --col=v16col col:v16col real 5m 9.94s user 0m 4.83s sys 0m 17.40s /home/go/app/mysql # time go run update.go --col=v64col col:v64col real 5m 11.56s user 0m 4.59s sys 0m 17.44s /home/go/app/mysql # time go run update.go --col=v255col col:v255col real 5m 9.64s user 0m 4.54s sys 0m 17.29s /home/go/app/mysql # time go run update.go --col=v256col col:v256col real 5m 20.83s user 0m 4.77s sys 0m 17.86s /home/go/app/mysql # ソースコード /home/go/app/mysql # cat update.go package main import ( "database/sql" "flag" "fmt" "strconv" _ "github.com/go-sql-driver/mysql" ) func main() { f := flag.String("col", "icol", "col") flag.Parse() fmt.Printf("col:%s\n", *f) db, err := sql.Open("mysql", "root:mypass@(go.mysql.local:3307)/test") if err != nil { fmt.Println("db error.") panic(err) } defer db.Close() upd, err := db.Prepare(fmt.Sprintf("UPDATE sample1 SET %s = ? WHERE %s = ?", *f, *f)) if err != nil { fmt.Println(err) panic(err) } for i := 1; i < 100000; i++ { upd.Exec(i+100000, strconv.Itoa(i)) upd.Exec(i, strconv.Itoa(i+100000)) } } 結論 という事で検索条件としては大きな影響はないので、積極的に定義を変更しなくても良いと思います。 ただ、追加する時にはint型にする事も一考してみると良いかなと思いました!
- 投稿日:2021-12-05T16:35:41+09:00
【MySQL】NULLを含む比較演算でハマった
アドベントカレンダー10日目です! MySQLに特化した内容ではありませんが、最近NULLの比較でハマったので知識の整理も兼ねて書き残します。 NULLと比較演算 NULLと比較したらどうなるか? NULLとの比較結果は常にNULLです。 SQLの比較演算の結果は1(true)・0(false)・NULLの3種類です。 NULLと比較した場合、人間の目で見た真偽とは関係なく結果はNULLになります。 SELECT 'a' = 'a'; -- 1(true) SELECT 'b' = 'a'; -- 0(false) SELECT '' = 'a'; -- 0(false) SELECT NULL = 'a'; -- NULL, 偽に見えるが0(false)ではない SELECT NULL = NULL; -- NULL, 真に見えるが1(true)ではない 結果をtrue/falseで受け取るには? 演算の結果を1(true)・0(false)で受け取るためにはIS NULLやIS NOT NULLなどを使用します。 SELECT NULL IS NULL; -- 1(true) SELECT NULL IS NOT NULL; -- 0(false) また、<=>(NULL安全等価演算子)を使用する方法もあります。 NULL以外の値の比較では=(等価演算子)と同じ挙動ですが、NULL同士の比較であれば1(true)が、片方のみNULLであれば0(false)の結果が返ります。 SELECT 'a' <=> 'a', 'b' <=> 'a', '' <=> 'a'; -- 1(true), 0(false), 0(false) SELECT NULL <=> NULL; -- 1(true) SELECT NULL <=> 'a'; -- 0(false) ハマったこと <>(不等価演算子)やNOT演算を利用した場合、比較対象のカラムにNULLが含まれると意図しない挙動が発生します。 実際にテーブルを用意して検証します。 テーブル・レコードの生成 mysql> CREATE TABLE users(id int, name varchar(10)); mysql> INSERT INTO users VALUES(1, 'taro'), (2, 'hanako'), (3, ''), (4, NULL); mysql> SELECT * FROM users; +------+--------+ | id | name | +------+--------+ | 1 | taro | | 2 | hanako | | 3 | | | 4 | NULL | +------+--------+ SELECT文を実行 <>(不等価演算子)もしくはNOT演算を利用してname = 'taro'以外のレコードを取得します。 id = 2, 3, 4のレコードが取得できればOKです。 NG id = 4のレコードが取得できません。 抽出対象はname <> 'taro'の結果が1(true)のレコードのみです。 id = 4のレコードはnameの値がNULLなのでname <> 'taro'の結果がNULLになり、抽出から除外されます。 mysql> SELECT * FROM users WHERE name <> 'taro'; -- SELECT * FROM users WHERE NOT(name = 'taro')も同様 +------+--------+ | id | name | +------+--------+ | 2 | hanako | | 3 | | +------+--------+ OK id = 4のレコードも含めて取得できました。 IS NULLの条件追加によりnameの値がNULLの場合も1(ture)が得られ、抽出対象となります。 mysql> SELECT * FROM users WHERE name <> 'taro' OR name IS NULL; +------+--------+ | id | name | +------+--------+ | 2 | hanako | | 3 | | | 4 | NULL | +------+--------+ ハマらないためにできること SQLを実行する度にNULLを想定するのではなく、カラムの定義で解決できれば楽ですね。 カラムにNOT NULL制約を追加する カラムにデフォルト値を設定する 常にNULLを想定したSQLを作成する 参考 MySQL 5.6 リファレンスマニュアル 12.3.2 比較関数と演算子 採用PR 弊社で一緒に働く仲間を募集しています。 全てのオタクを幸せにしたい方、是非ご覧ください! アドベントカレンダーも中盤に差し掛かりました。 クリスマスまで楽しんでくださいね!
- 投稿日:2021-12-05T15:39:55+09:00
DockerでCentOS7(PHP7.4 Apatch2.4 MySQL8)を構築してSSHで外部から接続するまで
CentOS7 説明 CentOS7のコンテナ内でphpとapatchを構成し、MySQLは別のコンテナを用意してCentOSから接続をする。 ディレクトリ構成 centos ├── Dockerfile └── php └── php.ini docker-compose.yml html └── index.php MySQL └── data docker-compose.yml version: '3.0' services: web: build: "./centos7" container_name: "centos7" ports: - 80:80 - 20022:20022 volumes: - "./html:/var/www/html" command: /sbin/init privileged: true db: image: mysql:8.0 environment: MYSQL_ALLOW_EMPTY_PASSWORD: 'yes' ports: - "3306:3306" volumes: - ./mysql/data:/var/lib/mysql depends_on: - web Dockerfile FROM centos:7 RUN yum -y update && yum clean all # Repository # EPEL RUN yum install -y epel-release # remi RUN yum -y install http://rpms.famillecollet.com/enterprise/remi-release-7.rpm # Apache2.4 インストール RUN yum -y install httpd # Apacheの自動起動設定 RUN systemctl enable httpd # PHP7.4 インストール RUN yum -y install --enablerepo=remi,remi-php74 php php-devel php-mbstring php-pdo php-xml php-gd php-fpm php-mysqlnd php-opcache php-pecl-zip libzip5 # php.iniコピー # COPY ./php/php.ini /etc/php.ini # vim インストール RUN yum -y install vim # SSH接続ここから-------- RUN yum -y update \ && yum install -y openssh-server \ openssh-clients \ && yum clean all # rootでのログインを許可 # ポートを22から20022に変更 # rootのパスワードをpasswordに設定 RUN sed -ri 's/^#PermitRootLogin yes/PermitRootLogin yes/' /etc/ssh/sshd_config \ && sed -ri 's/^#Port 22/Port 20022/' /etc/ssh/sshd_config \ && echo 'root:ronaldo7' | chpasswd EXPOSE 20022 # SSH接続ここまで-------- CMD ["/sbin/init"] php.ini [Date] date.timezone = "Asia/Tokyo"; [mbstring] mbstring.internal_encording = "UTF-8" mbstring.language = "Japanese" 1. CentOS7インストール 参考URL MySQL 1. MySQLインストール 参考URL 2. MySQL設定 参考URL 3. PDO接続確認 参考URL エラー備忘録 1. PDO接続エラー SQLSTATE[HY000] [2002] No such file or directory 対応手順 1. コンテナに接続 docker container exec -it mydoker_db_1 /bin/bash 2. ホストipアドレス確認 root@a81b73e7516c:/# cat /etc/hosts //ipアドレス確認 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters 172.30.0.3 a81b73e7516c 3. PDOのホスト名に確認したipアドレスを入力 define('HOSTNAME', '172.30.0.3'); SSH接続 1. ssh接続設定 参考URL エラー備忘録 1. composeを再構築した時に発生 //compose再構築 docker-compose build //sshで接続 ssh -p 20022 root@localhost //エラー @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! Someone could be eavesdropping on you right now (man-in-the-middle attack)! It is also possible that a host key has just been changed. The fingerprint for the ECDSA key sent by the remote host is SHA256:SoL3yQ5xw8FyrICsKI46vZRiaG1wd4BpnuBrjNO4bJ0. Please contact your system administrator. Add correct host key in /Users/user/.ssh/known_hosts to get rid of this message. Offending ECDSA key in /Users/user/.ssh/known_hosts:1 ECDSA host key for [localhost]:20022 has changed and you have requested strict checking. Host key verification failed. 対処方法 1. ローカルPCの.sshにあるknown_hostsファイルを削除 2. ssh再接続すると新しいknown_hostsが作成され、接続できる 参考URL apatch 備忘録 1.htaccessが効かない 対処方法 httpd.confのAllowOverride Noneの設定をNONEからALLに変更 AllowOverride None ↓ AllowOverride ALL 参考URL 使ったコマンド //起動中のコンテナ確認 docker ps //全てのコンテナ確認 docker ps -a //compose起動 docker-compose up -d //compose停止 docker-compose stop //compose再構築(設定ファイル等を編集した時) docker-compose build //compose再起動 docker-compose restart //コンテナに接続 docker container exec -it mydoker_db_1 /bin/bash docker exec -it centos7 /bin/bash //apatch起動 systemctl start httpd.service systemctl restart httpd //apatch状態確認 systemctl status httpd.service //sshd再起動 systemctl restart sshd //ホストipアドレス確認 cat /etc/hosts //vimインストール yum install vim //エラー表示 ini_set( 'display_errors', 1 );
- 投稿日:2021-12-05T00:27:43+09:00
MySQLのテーブル定義をMarkdown形式に出力するシェル
※ペライチアドベントカレンダー (12/5)の記事になります! 背景 今所属している株式会社ペライチ では esaというサービスでドキュメント管理をしています。 Qiitaと同じくMarkdown形式で記載します。 DB定義のページの更新を楽にしたく、ちょっとシェルを組んでみました。 サンプルDB CREATE TABLE `departments` ( `dept_no` char(4) NOT NULL COMMENT '部署No', `dept_name` varchar(40) NOT NULL COMMENT '部署名', PRIMARY KEY (`dept_no`), UNIQUE KEY `dept_name` (`dept_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='部署テーブル'; CREATE TABLE `dept_emp` ( `emp_no` int(11) NOT NULL COMMENT '社員No', `dept_no` char(4) NOT NULL COMMENT '部署No', `from_date` date NOT NULL COMMENT '開始日', `to_date` date NOT NULL COMMENT '終了日', PRIMARY KEY (`emp_no`,`dept_no`), KEY `dept_no` (`dept_no`), CONSTRAINT `dept_emp_ibfk_1` FOREIGN KEY (`emp_no`) REFERENCES `employees` (`emp_no`) ON DELETE CASCADE, CONSTRAINT `dept_emp_ibfk_2` FOREIGN KEY (`dept_no`) REFERENCES `departments` (`dept_no`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配属テーブル'; CREATE TABLE `employees` ( `emp_no` int(11) NOT NULL COMMENT '社員No', `birth_date` date NOT NULL COMMENT '生年月日', `first_name` varchar(14) NOT NULL COMMENT '姓', `last_name` varchar(16) NOT NULL COMMENT '名', `gender` enum('M','F') NOT NULL COMMENT '性別', `hire_date` date NOT NULL COMMENT '雇用日', PRIMARY KEY (`emp_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='社員テーブル'; CREATE TABLE `schema_migrations` ( `version` varchar(255) CHARACTER SET utf8 NOT NULL, PRIMARY KEY (`version`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; MySQL公式サンプル https://dev.mysql.com/doc/index-other.html employee dataのテーブルデータを一部拝借しました。 シェルスクリプト ファイル名はご自由に。 仮にdb_ddl_markdown.shとします。 #!/bin/sh # MySQL接続情報 DB_USER='root' DB_PASS='root' DB_HOST='127.0.0.1' DB_PORT='3306' DB_SCHEMA='employees' MYSQL_CONNECT="mysql --default-character-set=utf8 -u${DB_USER} -p${DB_PASS} -h${DB_HOST} -P${DB_PORT} ${DB_SCHEMA}" # 出力不要のテーブル IGNORE_TABLES=(schema_migrations others) # Markdown形式で出力 echo "## DB構成" echo { for TABLE in $(sh -c "${MYSQL_CONNECT} -N -e 'show tables'");do for IGNORE in "${IGNORE_TABLES[@]}"; do if [[ "$IGNORE" = "$TABLE" ]]; then continue 2 fi done echo "### ${TABLE}" echo COLUMNS=$(sh -c "${MYSQL_CONNECT} -t -e 'show full columns from ${TABLE}'") echo "${COLUMNS}" | sed -e '1d;$d;s/+/|/g' | awk -F"|" '{print "|"$2"|"$3"|"$5"|"$6"|"$7"|"$8"|"$10"|" }' echo done } 2> /dev/null 出力結果 $ sh db_ddl_markdown.sh ## DB構成 ### departments | Field | Type | Null | Key | Default | Extra | Comment | |-----------|-------------|------|-----|---------|-------|-----------| | dept_no | char(4) | NO | PRI | NULL | | 部署No | | dept_name | varchar(40) | NO | UNI | NULL | | 部署名 | ### dept_emp | Field | Type | Null | Key | Default | Extra | Comment | |-----------|---------|------|-----|---------|-------|-----------| | emp_no | int(11) | NO | PRI | NULL | | 社員No | | dept_no | char(4) | NO | PRI | NULL | | 部署No | | from_date | date | NO | | NULL | | 開始日 | | to_date | date | NO | | NULL | | 終了日 | ### employees | Field | Type | Null | Key | Default | Extra | Comment | |------------|---------------|------|-----|---------|-------|--------------| | emp_no | int(11) | NO | PRI | NULL | | 社員No | | birth_date | date | NO | | NULL | | 生年月日 | | first_name | varchar(14) | NO | | NULL | | 姓 | | last_name | varchar(16) | NO | | NULL | | 名 | | gender | enum('M','F') | NO | | NULL | | 性別 | | hire_date | date | NO | | NULL | | 雇用日 | 解説 MYSQL_CONNECT="mysql --default-character-set=utf8 -u${DB_USER} -p${DB_PASS} -h${DB_HOST} -P${DB_PORT} ${DB_SCHEMA}" まずはベースのMySQL接続コマンドを用意します。 サンプルなので愚直にユーザ名やパスワードをオプションに入れてますが --defaults-extra-fileオプションを使ってソースコードには書かない方がいいでしょうね。 for TABLE in $(sh -c "${MYSQL_CONNECT} -N -e 'show tables'");do -eオプションに実行させるSQLを書き、先のMySQL接続コマンドとつなげてコマンドの文字列を作成 sh -cで文字列の内容を実行します。 -Nオプションでカラム名無しでデータだけ出力されます。この部分だけの出力内容はこんな感じ。 departments dept_emp employees schema_migrations ここでは罫線は入ってきませんので、実行結果を for 変数名 in $(...) とループさせてテーブルごとに処理していきます。 for IGNORE in "${IGNORE_TABLES[@]}"; do if [[ "$IGNORE" = "$TABLE" ]]; then continue 2 fi done フレームワークで使うマイグレーションやログ用のテーブルなど、出力不要なテーブルだった場合、処理をスキップします 設定部分は IGNORE_TABLES=(schema_migrations others) です。 COLUMNS=$(sh -c "${MYSQL_CONNECT} -t -e 'show full columns from ${TABLE}'") テーブルごとにカラム情報を取得します。 descでもいいのでは?と思うかもしれませんが、カラムのCOMMENTの内容を撮りたいので show full columns fromで行います。 -tオプションで今度は罫線も含めて結果を得ます。 echo "${COLUMNS}" | sed -e '1d;$d;s/+/|/g' | awk -F"|" '{print "|"$2"|"$3"|"$5"|"$6"|"$7"|"$8"|"$10"|" }' ${COLUMNS}にカラム情報が入っているので Markdown形式に加工していきます。 echo "${COLUMNS}"だとテキストはまだ下記の感じです。 これをパイプ|で次のコマンドへと渡します。 +-----------+-------------+--------------------+------+-----+---------+-------+---------------------------------+-----------+ | Field | Type | Collation | Null | Key | Default | Extra | Privileges | Comment | +-----------+-------------+--------------------+------+-----+---------+-------+---------------------------------+-----------+ | dept_no | char(4) | utf8mb4_general_ci | NO | PRI | NULL | | select,insert,update,references | 部署No | | dept_name | varchar(40) | utf8mb4_general_ci | NO | UNI | NULL | | select,insert,update,references | 部署名 | +-----------+-------------+--------------------+------+-----+---------+-------+---------------------------------+-----------+ sed -e '1d;$d;s/+/|/g' sedはテキスト変換によく使われるコマンド。 呪文めいてますが、、 1d;は1行目削除、$d;は最後の行を削除、s/+/|/gは+を|に変換するパラメータです。この段階を通るとテキストの内容はこうなります。 | Field | Type | Collation | Null | Key | Default | Extra | Privileges | Comment | |-----------|-------------|--------------------|------|-----|---------|-------|---------------------------------|-----------| | dept_no | char(4) | utf8mb4_general_ci | NO | PRI | NULL | | select,insert,update,references | 部署No | | dept_name | varchar(40) | utf8mb4_general_ci | NO | UNI | NULL | | select,insert,update,references | 部署名 | Markdown形式のテーブルの書き方になりましたね。 ただ、情報量が多いので絞りましょう。 awk -F"|" '{print "|"$2"|"$3"|"$5"|"$6"|"$7"|"$8"|"$10"|" }' awkは先のsedと同じくテキスト処理でよく使われますが、 プログラミング言語としても使えて、演算やループなど凝ったことができます。 今回のは -Fオプションで区切り文字を指定し、printで列番号を指定して出力する、よく使われる書き方です。 |区切りだと一番左がパイプの外が1番目(空値)、Fieldが2番目になります。 | Field | Type | Null | Key | Default | Extra | Comment | |-----------|-------------|------|-----|---------|-------|-----------| | dept_no | char(4) | NO | PRI | NULL | | 部署No | | dept_name | varchar(40) | NO | UNI | NULL | | 部署名 | 無事、Markdown形式にできました。 先の出力結果を貼り付けると↓になります。 DB構成 departments Field Type Null Key Default Extra Comment dept_no char(4) NO PRI NULL 部署No dept_name varchar(40) NO UNI NULL 部署名 dept_emp Field Type Null Key Default Extra Comment emp_no int(11) NO PRI NULL 社員No dept_no char(4) NO PRI NULL 部署No from_date date NO NULL 開始日 to_date date NO NULL 終了日 employees Field Type Null Key Default Extra Comment emp_no int(11) NO PRI NULL 社員No birth_date date NO NULL 生年月日 first_name varchar(14) NO NULL 姓 last_name varchar(16) NO NULL 名 gender enum('M','F') NO NULL 性別 hire_date date NO NULL 雇用日 } 2> /dev/null シェルのテクニックの一つ。 {}で複数行を囲んで一括りの処理とします。 mysqlコマンドを実行したときに、サンプルの書き方だとパスワードに関して警告が出るので、 エラー出力を無視するリダイレクトを入れてます。 デバッグ時には外してください。 最後に シェルはもう少し改良の余地があるなーと思いつつ。。 次は 12/7 ペライチCTO @katsukii さんの「ペライチ開発チームのご紹介」になります!