ER図の自動生成について、dbdiagram.io, DBeaver, A5M2 を比較してみる。

# はじめに

データベース設計のER図について、自動で生成する以下３つのツールを比較した記事です。

- dbdiagram.io
- DBeaver
- A5:SQL Mk-2（A5M2）

<br>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/d98e7b74-febc-a192-750c-2a3ad5969cbb.png)

https://qiita.com/ryamate/items/3779418172c4f5a83212

先日、こちらの記事をQiitaに投稿したところ、多くの方に記事を見ていただき、コメントも多数いただきました。

ER図に関するお勧めのツールをコメントいただく方が多くいらっしゃいました。

今回はその中から、無料でも利用できる3つのツールの「ER図の自動生成」の機能を試します。
<br>

比較の結論としては、〇〇が一番良いという感想ではなく、どのツールも多機能で、できることは違うので、今後使うときは用途や業務の環境によって使い分けていけたらと思っています。

<br>

## 目次

それぞれのツールについて、下記の内容を書いていきます。

[1. dbdiagram.io](#1-dbdiagramio)
　[1-1. 始める](#1-1-始める)
　[1-2. 使う](#1-2-使う)
　[1-3. 感想](#1-3-感想)
[2. DBeaver](#2-dbeaver)
　[2-1. 始める](#2-1-始める)
　[2-2. 使う](#2-2-使う)
　[2-3. 感想](#2-3-感想)
[3. A5:SQL Mk-2（A5M2）](#3-a5sql-mk-2a5m2)
　[3-1. 始める](#3-1-始める)
　[3-2. 使う](#3-2-使う)
　[3-3. 感想](#3-3-感想)

## 環境

- macOS Big Sur バージョン: 11.6
- Homebrew バージョン: 3.5.4
- DBeaver バージョン: 22.1.4
- A5:SQL Mk-2（A5M2） バージョン: 2.17.2

前回の記事に引き続き、ポートフォリオとしてのWebアプリの Docker, MySQL で作成したデータベースをサンプルにします。

https://qiita.com/ryamate/items/c3b4f63f8d94ab090bf8

https://github.com/ryamate/yonde-web-app

<br>

<br>

# 1. dbdiagram.io

> dbdiagram.io は、DSL コードを入力してデータベース ダイアグラム (ERD) を描画する無料のシンプルなツールです。dbdiagram は、一般的な DBML (データベース マークアップ言語) を使用します。
> 

https://dbdiagram.io/docs/#dbdiagram-docs

dbdiagram.io でできることは、

- SQL ダンプファイル（DDL） から ダイアグラム（ER図） をすばやく生成できる
- SQL ステートメント（DDL） を直接生成して、データベースのテーブルを作成できる

などです。

```mermaid
flowchart LR
	subgraph dbdiagram.io
		direction LR
			DBML --> id1[ER図]
	end
	DDL --> id1[ER図]
	id1[ER図] --> DDL
	テーブル --> |spldump| DDL
	DDL --> テーブル

	style id1 fill:#F6CE00,color:#000
```

## 1-1. 始める

アプリのページにアクセスして、Googleアカウントなどでサインインしたら、ブラウザ上で使用をすぐに開始することができます。

https://dbdiagram.io/home

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/311f7f3d-e78d-6b87-28b0-4e7b8f572623.png)

<br>

## 1-2. 使う

以下の手順で、ER図を自動生成します。

[① SQLダンプファイル（DDL）の作成](#-sqlダンプファイルddlの作成)
[② ER図 の生成](#-er図-の生成)

```mermaid
flowchart LR
	subgraph dbdiagram.io
		direction LR
			DBML -.-> id1[ER図]
	end
	DDL -->|2.| id1[ER図]
	id1[ER図] -.-> DDL
	テーブル --> |1.spldump| DDL
	DDL -.-> テーブル

	style id1 fill:#F6CE00,color:#000
```

### ① SQLダンプファイル（DDL）の作成

まずは、SQLダンプファイル（DDL） を Sequel Ace（macOS の MySQL/MariaDB データベース管理ツール ）で用意しました。（さっそく別のツールを使っています…）

https://qiita.com/ryamate/items/ede95aca62cca1ecdf86

<br>

dbdiagram.io への import 用に作成した SQLダンプファイル はこちらです。

<details><summary>dbdiagram.io への import 用の SQLダンプファイル</summary>
    
上記記事の方法で作成しましたが、事前準備として、`#` のコメントアウトはSQLでエラーになるため、 `#` で始まる全行を削除しました。
    
また、`LOCK` と `UNLOCK` は dbdiagram.io への import 時にエラーになるため、削除しました。

```sql
/*!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 */;
SET NAMES utf8mb4;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE='NO_AUTO_VALUE_ON_ZERO', SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

DROP TABLE IF EXISTS `child_read_record`;

CREATE TABLE `child_read_record` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `read_record_id` bigint unsigned NOT NULL,
  `child_id` bigint unsigned NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `child_read_record_read_record_id_foreign` (`read_record_id`),
  KEY `child_read_record_child_id_foreign` (`child_id`),
  CONSTRAINT `child_read_record_child_id_foreign` FOREIGN KEY (`child_id`) REFERENCES `children` (`id`) ON DELETE CASCADE,
  CONSTRAINT `child_read_record_read_record_id_foreign` FOREIGN KEY (`read_record_id`) REFERENCES `read_records` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

DROP TABLE IF EXISTS `children`;

CREATE TABLE `children` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `family_id` bigint unsigned NOT NULL,
  `name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `gender_code` int DEFAULT NULL,
  `birthday` date DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `children_family_id_foreign` (`family_id`),
  CONSTRAINT `children_family_id_foreign` FOREIGN KEY (`family_id`) REFERENCES `families` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- LOCK TABLES `children` WRITE;
/*!40000 ALTER TABLE `children` DISABLE KEYS */;

INSERT INTO `children` (`id`, `family_id`, `name`, `gender_code`, `birthday`, `created_at`, `updated_at`)
VALUES
	(1,1,'ゆきまさ',1,'2020-04-01','2022-07-15 06:45:00','2022-07-15 06:45:00'),
	(2,1,'みくり',2,'2021-04-01','2022-07-15 06:45:00','2022-07-15 06:45:00'),
	(3,2,'お子さま',0,NULL,'2022-07-15 06:50:06','2022-07-15 06:50:06');

/*!40000 ALTER TABLE `children` ENABLE KEYS */;
-- UNLOCK TABLES;

DROP TABLE IF EXISTS `contacts`;

CREATE TABLE `contacts` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `nickname` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `title` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `body` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

DROP TABLE IF EXISTS `failed_jobs`;

CREATE TABLE `failed_jobs` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `connection` text COLLATE utf8mb4_unicode_ci NOT NULL,
  `queue` text COLLATE utf8mb4_unicode_ci NOT NULL,
  `payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
  `exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
  `failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

DROP TABLE IF EXISTS `families`;

CREATE TABLE `families` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
  `nickname` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `introduction` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `families_name_unique` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- LOCK TABLES `families` WRITE;
/*!40000 ALTER TABLE `families` DISABLE KEYS */;

INSERT INTO `families` (`id`, `name`, `nickname`, `introduction`, `created_at`, `updated_at`)
VALUES
	(1,'guest','ゲスト','ゲストログインユーザー用です。よろしくお願いします。','2022-07-15 06:45:00','2022-07-15 06:45:00'),
	(2,'y6E2867HyrBuG9fT','よんで','よろしくお願いします。','2022-07-15 06:50:06','2022-07-15 06:50:06');

/*!40000 ALTER TABLE `families` ENABLE KEYS */;
-- UNLOCK TABLES;

DROP TABLE IF EXISTS `follows`;

CREATE TABLE `follows` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `user_id` bigint unsigned NOT NULL,
  `family_id` bigint unsigned NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `follows_user_id_foreign` (`user_id`),
  KEY `follows_family_id_foreign` (`family_id`),
  CONSTRAINT `follows_family_id_foreign` FOREIGN KEY (`family_id`) REFERENCES `families` (`id`) ON DELETE CASCADE,
  CONSTRAINT `follows_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

DROP TABLE IF EXISTS `invites`;

CREATE TABLE `invites` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `family_id` bigint unsigned NOT NULL,
  `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `token` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `invites_token_unique` (`token`),
  KEY `invites_family_id_foreign` (`family_id`),
  CONSTRAINT `invites_family_id_foreign` FOREIGN KEY (`family_id`) REFERENCES `families` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- LOCK TABLES `invites` WRITE;
/*!40000 ALTER TABLE `invites` DISABLE KEYS */;

INSERT INTO `invites` (`id`, `family_id`, `email`, `token`, `created_at`, `updated_at`)
VALUES
	(1,2,'test2@test','9UyZzBbWeNL41Dcr','2022-07-15 06:51:23','2022-07-15 06:51:23');

/*!40000 ALTER TABLE `invites` ENABLE KEYS */;
-- UNLOCK TABLES;

DROP TABLE IF EXISTS `likes`;

CREATE TABLE `likes` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `user_id` bigint unsigned NOT NULL,
  `picture_book_id` bigint unsigned NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `likes_user_id_foreign` (`user_id`),
  KEY `likes_picture_book_id_foreign` (`picture_book_id`),
  CONSTRAINT `likes_picture_book_id_foreign` FOREIGN KEY (`picture_book_id`) REFERENCES `picture_books` (`id`) ON DELETE CASCADE,
  CONSTRAINT `likes_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

DROP TABLE IF EXISTS `migrations`;

CREATE TABLE `migrations` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `migration` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `batch` int NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- LOCK TABLES `migrations` WRITE;
/*!40000 ALTER TABLE `migrations` DISABLE KEYS */;

INSERT INTO `migrations` (`id`, `migration`, `batch`)
VALUES
	(1,'2014_10_12_100000_create_password_resets_table',1),
	(2,'2019_08_19_000000_create_failed_jobs_table',1),
	(3,'2021_03_02_171654_create_families_table',1),
	(4,'2021_03_02_171655_create_users_table',1),
	(5,'2021_03_02_235959_create_children_table',1),
	(6,'2021_03_03_000000_create_picture_books_table',1),
	(7,'2021_03_05_000000_create_read_records_table',1),
	(8,'2021_03_06_000000_create_child_read_record_table',1),
	(9,'2021_05_10_155314_create_tags_table',1),
	(10,'2021_05_10_171704_create_read_record_tag_table',1),
	(11,'2021_05_11_132606_create_follows_table',1),
	(12,'2021_05_31_235959_create_likes_table',1),
	(13,'2021_06_09_180935_create_invites_table',1),
	(14,'2021_07_09_205018_create_contacts_table',1),
	(15,'2021_07_18_145514_add_column_soft_deletes_users_table',1);

/*!40000 ALTER TABLE `migrations` ENABLE KEYS */;
-- UNLOCK TABLES;

DROP TABLE IF EXISTS `password_resets`;

CREATE TABLE `password_resets` (
  `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `token` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  KEY `password_resets_email_index` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

DROP TABLE IF EXISTS `picture_books`;

CREATE TABLE `picture_books` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `family_id` bigint unsigned NOT NULL,
  `user_id` bigint unsigned NOT NULL,
  `google_books_id` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
  `isbn_13` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `authors` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `published_date` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `description` varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `thumbnail_url` varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `five_star_rating` int NOT NULL,
  `read_status` int NOT NULL,
  `review` varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `picture_books_family_id_foreign` (`family_id`),
  KEY `picture_books_user_id_foreign` (`user_id`),
  CONSTRAINT `picture_books_family_id_foreign` FOREIGN KEY (`family_id`) REFERENCES `families` (`id`),
  CONSTRAINT `picture_books_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

DROP TABLE IF EXISTS `read_record_tag`;

CREATE TABLE `read_record_tag` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `read_record_id` bigint unsigned NOT NULL,
  `tag_id` bigint unsigned NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `read_record_tag_read_record_id_foreign` (`read_record_id`),
  KEY `read_record_tag_tag_id_foreign` (`tag_id`),
  CONSTRAINT `read_record_tag_read_record_id_foreign` FOREIGN KEY (`read_record_id`) REFERENCES `read_records` (`id`) ON DELETE CASCADE,
  CONSTRAINT `read_record_tag_tag_id_foreign` FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

DROP TABLE IF EXISTS `read_records`;

CREATE TABLE `read_records` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `picture_book_id` bigint unsigned NOT NULL,
  `family_id` bigint unsigned NOT NULL,
  `user_id` bigint unsigned NOT NULL,
  `memo` varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `read_date` date NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `read_records_picture_book_id_foreign` (`picture_book_id`),
  KEY `read_records_family_id_foreign` (`family_id`),
  KEY `read_records_user_id_foreign` (`user_id`),
  CONSTRAINT `read_records_family_id_foreign` FOREIGN KEY (`family_id`) REFERENCES `families` (`id`) ON DELETE CASCADE,
  CONSTRAINT `read_records_picture_book_id_foreign` FOREIGN KEY (`picture_book_id`) REFERENCES `picture_books` (`id`) ON DELETE CASCADE,
  CONSTRAINT `read_records_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

DROP TABLE IF EXISTS `tags`;

CREATE TABLE `tags` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `tags_name_unique` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

DROP TABLE IF EXISTS `users`;

CREATE TABLE `users` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `family_id` bigint unsigned NOT NULL,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `nickname` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `email_verified_at` timestamp NULL DEFAULT NULL,
  `password` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `icon_path` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `relation` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  `deleted_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `users_name_unique` (`name`),
  UNIQUE KEY `users_email_unique` (`email`,`deleted_at`),
  KEY `users_family_id_foreign` (`family_id`),
  CONSTRAINT `users_family_id_foreign` FOREIGN KEY (`family_id`) REFERENCES `families` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- LOCK TABLES `users` WRITE;
/*!40000 ALTER TABLE `users` DISABLE KEYS */;

INSERT INTO `users` (`id`, `family_id`, `name`, `nickname`, `email`, `email_verified_at`, `password`, `remember_token`, `icon_path`, `relation`, `created_at`, `updated_at`, `deleted_at`)
VALUES
	(1,1,'guest_user','げん(ゲスト)','guest_user@guest.com','2022-07-15 06:45:01',NULL,NULL,'image/1.jpg','パパ','2022-07-15 06:45:01','2022-07-15 06:45:01',NULL),
	(2,1,'guest_partner','ゆい(パートナー)','guest_partner@guest.com','2022-07-15 06:45:01',NULL,NULL,'image/2.jpg','ママ','2022-07-15 06:45:01','2022-07-15 06:45:01',NULL),
	(3,2,'kWJ1A7fhlzUZXDAb','やまて','test@test','2022-07-15 06:50:41','$2y$10$JCbzpFTNRTTbYfh9jxp.u.0JB6AETjnJitwofj3tv4Mi7Gn/GyCTm',NULL,NULL,NULL,'2022-07-15 06:50:06','2022-07-15 06:50:41',NULL);

/*!40000 ALTER TABLE `users` ENABLE KEYS */;
-- UNLOCK TABLES;

/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_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 */;
```
</details>

<br>

### ② ER図 の生成

dbdiagram.io を開きます（サインインします）。

https://dbdiagram.io/home

SQLダンプファイル から ER図 を生成するには、

画面上部ツールバー「Import」→「Import from MySQL」を押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/671e4f50-8894-5d61-fc2c-8d5fe26bca01.png)

次の画面で、先程①で用意した SQLダンプファイル をアップロードします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/a78aa0be-71ff-a390-f7fc-1edb6b125146.png)

アップロードが完了したら、自動生成が完了しており、右側に ER図 が表示され、左側にER図の元となっている DBML が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/ed43e7dc-df6a-9c4d-7598-5bbc57a1895b.png)

<details><summary>自動生成された DBML 全文</summary>

```
Table "child_read_record" {
  "id" bigint [pk, not null, increment]
  "read_record_id" bigint [not null]
  "child_id" bigint [not null]
  "created_at" timestamp [default: NULL]
  "updated_at" timestamp [default: NULL]

Indexes {
  read_record_id [name: "child_read_record_read_record_id_foreign"]
  child_id [name: "child_read_record_child_id_foreign"]
}
}

Table "children" {
  "id" bigint [pk, not null, increment]
  "family_id" bigint [not null]
  "name" varchar(100) [default: NULL]
  "gender_code" int [default: NULL]
  "birthday" date [default: NULL]
  "created_at" timestamp [default: NULL]
  "updated_at" timestamp [default: NULL]

Indexes {
  family_id [name: "children_family_id_foreign"]
}
}

Table "contacts" {
  "id" bigint [pk, not null, increment]
  "email" varchar(255) [not null]
  "nickname" varchar(255) [not null]
  "name" varchar(255) [default: NULL]
  "title" varchar(255) [default: NULL]
  "body" varchar(255) [not null]
  "created_at" timestamp [default: NULL]
  "updated_at" timestamp [default: NULL]
}

Table "failed_jobs" {
  "id" bigint [pk, not null, increment]
  "connection" text [not null]
  "queue" text [not null]
  "payload" longtext [not null]
  "exception" longtext [not null]
  "failed_at" timestamp [not null, default: `CURRENT_TIMESTAMP`]
}

Table "families" {
  "id" bigint [pk, not null, increment]
  "name" varchar(100) [not null]
  "nickname" varchar(255) [default: NULL]
  "introduction" varchar(255) [default: NULL]
  "created_at" timestamp [default: NULL]
  "updated_at" timestamp [default: NULL]

Indexes {
  name [unique, name: "families_name_unique"]
}
}

Table "follows" {
  "id" bigint [pk, not null, increment]
  "user_id" bigint [not null]
  "family_id" bigint [not null]
  "created_at" timestamp [default: NULL]
  "updated_at" timestamp [default: NULL]

Indexes {
  user_id [name: "follows_user_id_foreign"]
  family_id [name: "follows_family_id_foreign"]
}
}

Table "invites" {
  "id" bigint [pk, not null, increment]
  "family_id" bigint [not null]
  "email" varchar(255) [not null]
  "token" varchar(16) [not null]
  "created_at" timestamp [default: NULL]
  "updated_at" timestamp [default: NULL]

Indexes {
  token [unique, name: "invites_token_unique"]
  family_id [name: "invites_family_id_foreign"]
}
}

Table "likes" {
  "id" bigint [pk, not null, increment]
  "user_id" bigint [not null]
  "picture_book_id" bigint [not null]
  "created_at" timestamp [default: NULL]
  "updated_at" timestamp [default: NULL]

Indexes {
  user_id [name: "likes_user_id_foreign"]
  picture_book_id [name: "likes_picture_book_id_foreign"]
}
}

Table "migrations" {
  "id" int [pk, not null, increment]
  "migration" varchar(255) [not null]
  "batch" int [not null]
}

Table "password_resets" {
  "email" varchar(255) [not null]
  "token" varchar(255) [not null]
  "created_at" timestamp [default: NULL]

Indexes {
  email [name: "password_resets_email_index"]
}
}

Table "picture_books" {
  "id" bigint [pk, not null, increment]
  "family_id" bigint [not null]
  "user_id" bigint [not null]
  "google_books_id" varchar(100) [not null]
  "isbn_13" varchar(100) [default: NULL]
  "title" varchar(255) [not null]
  "authors" varchar(255) [default: NULL]
  "published_date" varchar(100) [default: NULL]
  "description" varchar(1000) [default: NULL]
  "thumbnail_url" varchar(1000) [default: NULL]
  "five_star_rating" int [not null]
  "read_status" int [not null]
  "review" varchar(1000) [default: NULL]
  "created_at" timestamp [default: NULL]
  "updated_at" timestamp [default: NULL]

Indexes {
  family_id [name: "picture_books_family_id_foreign"]
  user_id [name: "picture_books_user_id_foreign"]
}
}

Table "read_record_tag" {
  "id" bigint [pk, not null, increment]
  "read_record_id" bigint [not null]
  "tag_id" bigint [not null]
  "created_at" timestamp [default: NULL]
  "updated_at" timestamp [default: NULL]

Indexes {
  read_record_id [name: "read_record_tag_read_record_id_foreign"]
  tag_id [name: "read_record_tag_tag_id_foreign"]
}
}

Table "read_records" {
  "id" bigint [pk, not null, increment]
  "picture_book_id" bigint [not null]
  "family_id" bigint [not null]
  "user_id" bigint [not null]
  "memo" varchar(1000) [default: NULL]
  "read_date" date [not null]
  "created_at" timestamp [default: NULL]
  "updated_at" timestamp [default: NULL]

Indexes {
  picture_book_id [name: "read_records_picture_book_id_foreign"]
  family_id [name: "read_records_family_id_foreign"]
  user_id [name: "read_records_user_id_foreign"]
}
}

Table "tags" {
  "id" bigint [pk, not null, increment]
  "name" varchar(255) [not null]
  "created_at" timestamp [default: NULL]
  "updated_at" timestamp [default: NULL]

Indexes {
  name [unique, name: "tags_name_unique"]
}
}

Table "users" {
  "id" bigint [pk, not null, increment]
  "family_id" bigint [not null]
  "name" varchar(255) [not null]
  "nickname" varchar(255) [default: NULL]
  "email" varchar(255) [not null]
  "email_verified_at" timestamp [default: NULL]
  "password" varchar(255) [default: NULL]
  "remember_token" varchar(100) [default: NULL]
  "icon_path" varchar(255) [default: NULL]
  "relation" varchar(100) [default: NULL]
  "created_at" timestamp [default: NULL]
  "updated_at" timestamp [default: NULL]
  "deleted_at" timestamp [default: NULL]

Indexes {
  name [unique, name: "users_name_unique"]
  (email, deleted_at) [unique, name: "users_email_unique"]
  family_id [name: "users_family_id_foreign"]
}
}

Ref "child_read_record_child_id_foreign":"children"."id" < "child_read_record"."child_id" [delete: cascade]

Ref "child_read_record_read_record_id_foreign":"read_records"."id" < "child_read_record"."read_record_id" [delete: cascade]

Ref "children_family_id_foreign":"families"."id" < "children"."family_id"

Ref "follows_family_id_foreign":"families"."id" < "follows"."family_id" [delete: cascade]

Ref "follows_user_id_foreign":"users"."id" < "follows"."user_id" [delete: cascade]

Ref "invites_family_id_foreign":"families"."id" < "invites"."family_id"

Ref "likes_picture_book_id_foreign":"picture_books"."id" < "likes"."picture_book_id" [delete: cascade]

Ref "likes_user_id_foreign":"users"."id" < "likes"."user_id" [delete: cascade]

Ref "picture_books_family_id_foreign":"families"."id" < "picture_books"."family_id"

Ref "picture_books_user_id_foreign":"users"."id" < "picture_books"."user_id"

Ref "read_record_tag_read_record_id_foreign":"read_records"."id" < "read_record_tag"."read_record_id" [delete: cascade]

Ref "read_record_tag_tag_id_foreign":"tags"."id" < "read_record_tag"."tag_id" [delete: cascade]

Ref "read_records_family_id_foreign":"families"."id" < "read_records"."family_id" [delete: cascade]

Ref "read_records_picture_book_id_foreign":"picture_books"."id" < "read_records"."picture_book_id" [delete: cascade]

Ref "read_records_user_id_foreign":"users"."id" < "read_records"."user_id" [delete: cascade]

Ref "users_family_id_foreign":"families"."id" < "users"."family_id"
```
</details>

テーブルの位置は DBML で記述したテーブルの順番が考慮されているようで、画像のER図のテーブル配置は、手作業で配置したいところに動かしました（動かした後の状態で保存されます）。

以下は Export してみた PNGファイル です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/2d187b2d-c2c9-685e-f16a-477fe727bad8.png)

<br>

## 1-3. 感想

- ER図のデザインは、シンプルでかっこよくとても見やすいと思いました。
- 図の作りやすさは SQLダンプファイル を用意して、export するだけなのでとても楽です。
- さらに、ER図 から DDL が生成できることも便利だと思います。
- DBML（dbdiagram.io の記法）で記述する場合は、記法を把握する学習コストはありますが、それほど難しくはなさそうな印象です。
- VSCodeの拡張機能にはなかったのと、GitHubにサポートされていない記法のようですので、 dbdiagram.io の画面上で基本的に作業することになります。
- チームでの利用（共同での編集）は有料なので、無料では export したファイルを共有する使い方かなと思います。

<br><br>

# 2. DBeaver

DBeaver はデータベース管理ツールです。

https://dbeaver.com/docs/wiki/ER-Diagrams/

有料版と無料版があり、無料版でも ER図の自動生成 はできました。

さまざまなデータベースに対応していて、かなり多機能なようです。今回は試してないのですが、ER図からDDLを生成することもできるようです。

```mermaid
flowchart LR
	subgraph DBeaver
		direction LR
		id1[ER図] --> DDL --> テーブル --> id1[ER図]
	end
	style id1 fill:#F6CE00,color:#000
```

## 2-1. 始める

DBeaver をダウンロードします。

https://dbeaver.io/download/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/c6a55569-5df5-e38a-87cd-20f9d04f56ff.png)

## 2-2. 使う

DBeaver アプリを開きます。 DBeaver で データベースに接続すれば、ER図をすぐに表示することができます。

[① データベースの追加（MySQL接続）](#-データベースの追加mysql接続)
[② ER図 の表示](#-er図-の表示)

### ① データベースの追加（MySQL接続）

DBeaver アプリを開いて、データベースと接続します。

データベースを選択して、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/9736c52c-a09f-ffeb-5615-3ef94258a0b6.png)

必要なドライバをダウンロードして、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/d492045a-5d36-b3e3-8e32-1f4cfb146c21.png)

データベース接続します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/f4bbb96a-ce56-ce0d-4b6b-5e7ef1a9a5e0.png)

### ② ER図 の表示

あとは左サイドバーでデータベースを選択して、「ER図」タブを開くだけです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/55dfdb3a-2d55-2a85-a78f-2c2d134d2b31.png)

## 2-3. 感想

- ER図のデザインはとても見やすいと思います。エンティティ（テーブル）の位置とリレーションシップ（線）の重なりが少なく自動生成されるため、手作業での調整がいらないと感じました。
- 見やすさは、主キーが右側で、外部キーが左側に統一されて表示されていることで読みやすいのかなと思います。
- 今回試したいのはER図を自動生成したいという一点でしたが、データベース管理ツールとしては、いろんなデータベースに対応していて、多機能ということなので、使いこなせたらとても便利なんだろうと思います。

<br><br>

# 3. A5:SQL Mk-2（A5M2）

> A5:SQL Mk-2は複雑化するデータベース開発を支援するために開発されたフリーのSQLクライアントです。
> 
> 
> 高機能かつ軽量で、使い方が分かりやすいことを目標に開発されています。
> 
> SQLを実行したり、テーブルを編集するほかに、SQLの実行計画を取得したり、ER図を作成したりすることが出来ます。
> 

https://a5m2.mmatsubara.com/

## 3-1. 始める

少し長くなるため、**A5M2**（**A5:SQL Mk-2**）を macOS で起動させるまでの手順をこちらの記事にまとめました。

https://qiita.com/ryamate/items/7c07881d066bbde4577a

<br>

## 3-2. 使う

以下の手順で、ER図を自動生成します。

[① データベースの追加（MySQL接続）](#-データベースの追加mysql接続-1)
[② ER図 の生成](#-er図-の生成-1)

### ① データベースの追加（MySQL接続）

A5M2 を起動します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/7b439a7d-c78e-ae66-ee53-dabfc3b2b059.png)

「レジストリ」を押すと、データベースの接続に進みます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/00ec50dc-97ad-6670-b63e-6dbfcded0410.png)

Webアプリ（ポートフォリオ）のデータベースと接続してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/5bdc3cb3-6fd5-3752-d7a7-d4f9b1f5dbfe.png)

### ② ER図 の生成

データベースに作成されている テーブル から、 ER図 を生成します。

```mermaid
flowchart LR
	subgraph A5M2
		direction LR
		id1[ER図] -.-> DDL -.-> id2[テーブル] -->|リバース生成| id1
	end
	style id1 fill:#F6CE00,color:#000
```

ツールバー「データベース」→「ER図」→「ER図のリバース生成」を押します。

テーブルを選択して、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/c362be92-cdfa-97dc-3c5c-faca2c9a9fbf.png)

「リバースER図生成」を押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/18cf9da7-d7a8-f7aa-61f6-a1c2ac9abc2f.png)

エンティティ（テーブル）の位置関係は手作業で位置を変更したものがこちらのER図です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/e67c4e96-e997-623c-d266-7f391b812ddf.png)

## 3-3. 感想

- A5M2 の GUI や ER図 のデザインはかなりシンプルですけど、テーブルからER図は自動生成できますし、ER図からそのとおりにテーブルを作成するためのDDLを生成することもでき、とても便利です。
- 開発を開始してから25年経過するらしいですが（すごい…）、メンテナンスもされていて現役のご様子です。
- Qiita記事とはてなブックマークでのコメントでは、 A5M2 を推す声が多かったように思いますが、とても納得できました。

<br><br>

# まとめ

今回は、3つのツールの「ER図の自動生成」についての機能を試しましたが、各ツールとも色々な機能があるので、今後、機能を把握して、用途に応じて使っていけたらと思っています。

<br>

ありがとうございました。