20200227のMySQLに関する記事は5件です。

多対多テーブルの作り方

データベース設計

投稿(postテーブル)に対してタグ(tagテーブル)付けができる機能を作るとします
一つの投稿は複数のタグを持つことができ、一つのタグは複数のタグを持つことができる場合、そのままアソシエーションを組んでしまうとデータベースの中身が複雑になってしまいます。
そのためどの投稿がどのタグに結びついているのかを記憶するテーブルを別に作り、整理します。

内容

postテーブルを作る

ターミナル
rails g model Post image:text

tagテーブルを作る

ターミナル
rails g model Tag name:string

中間テーブルを作る

中間テーブルは各モデルの複数形を名前をスネーク型で繋げます
今回の場合はPosts_tags
postとtagの結びつきを記憶するテーブルのため各idのカラムを作ります
refarencesの型はこの後記述するマイグレーションファイルに記載するforeighn_keyの制約に対応した型になります。
foreighn_keyはpost_idやtag_idカラムに対してpost_idに対応したpostsテーブルのidがあることを保証する制約です。
例)post_id:1 tag_id:1 であればpostsテーブルにid:1の投稿があり、tagsテーブルにもid:1があることを保証しています。

ターミナル
rails g model Posts_tags post_id:refaremces tag_id:refarences

アソシエーション

多対多テーブルのアソシエーションはthroughを使用します。
postとtagは関連しているがposts_tagsを経由して関連することを表します。

ppst.rb
has_many :posts_tags
has_many :tags, through: :posts_tags
tag.rb
has_many :posts_tags
has_many :posts, through: :posts_tags
posts_tag.rb
belongs_to :post 
belongs_to :tag

マイグレーションファイル

マイグレーションファイル
class CreatePostsTags < ActiveRecord::Migration[5.2]
  def change
    create_table :posts_tags do |t|
      t.refarence :post_id, foreign_key: true
      t.refarences :tag_id, foreign_key: true

      t.timestamps
    end
  end
end

完成

これで多対多テーブルの作成が完了します。
※sqliteでrefarences型を使用しようとしたところマイグレーションができませんでした。integer型に変更したところマイグレーションできたので、sqliteではrefarences型は使用できないようです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MySQL UNIQUE KEYの変更

概要

MySQLのUNIQUE KEYの変更がすんなりいかないということだけは記憶に残っていましたが、方法が記憶に残っていなかったので、備忘録的に記載します。

前提

以下のようなTableを想定します。

mysql> SHOW CREATE TABLE articles;

CREATE TABLE `articles` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `tab_id` int(10) unsigned NOT NULL,
  `articles` mediumtext NOT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_tab_id` (`tab_id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4

mysql> ALTER TABLE articles ADD COLUMN experiment_sign varchar(64) AFTER tab_id;

Query OK, 0 rows affected (0.27 sec)
Records: 0  Duplicates: 0  Warnings: 0

このarticles Tableにexperiment_sign Columnを追加したとします。

mysql> SHOW CREATE TABLE articles

CREATE TABLE `articles` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `tab_id` int(10) unsigned NOT NULL,
  `experiment_sign` varchar(64) NOT NULL,
  `articles` mediumtext NOT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_tab_id` (`tab_id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4

experiment_sign Columnがvarchar(64)で追加されているのが確認できると思います。
そして、UNIQUE KEYがtab_idのみになっていますが、これにexperiment_signを追加して、下記のような変更を行いたいと仮定します。

UNIQUE KEY `idx_tab_id (`tab_id`)

UNIQUE KEY `idx_tab_id_experiment_sign` (`tab_id`, `experiment_sign`)

さあ、このときどうするかというのが、今回のお話です。

失敗例

何も考えず、とりあえずUNIQUE KEY追加すればええやろと、安易に考えると、下記のようなALTER文を書いてしまいます。私です。

ALTER TABLE articles ADD UNIQUE `idx_tab_id_experiment_sign (`tab_id`, `experiment_sign`)

こうすると、どうなるかというと

CREATE TABLE `articles` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `tab_id` int(10) unsigned NOT NULL,
  `experiment_sign` varchar(64) NOT NULL,
  `articles` mediumtext NOT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_tab_id` (`tab_id`)
  UNIQUE KEY `idx_tab_id_experiment_sign` (`tab_id`, `experiment_sign`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4

とまあ、UNIQUE KEYが二つつきますね。はい。
これでは、わけわからんことになりますね。見た目も、無駄に誤解を招きます。

正解例

ちゃんと既存UNIQUE KEYを潰しましょう。

mysql> ALTER TABLE articles ADD UNIQUE `idx_tab_id_experiment_sign (`tab_id`, `experiment_sign`);
mysql> ALTER TABLE articles DELETE INDEX `idx_tab_id;

こうすると、理想の形ができあがります。

CREATE TABLE `articles` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `tab_id` int(10) unsigned NOT NULL,
  `experiment_sign` varchar(64) NOT NULL,
  `articles` mediumtext NOT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_tab_id_experiment_sign` (`tab_id`, `experiment_sign`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4

終わり

ちゃんと、理想の形になっているか確認しましょう。以上です。
ああ、もちろん、UNIQUE KEYはNOT NULL制約をつけましょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

dependent: :destroyをつけていても削除できなかった原因

長時間調べたが解決方法が見つからないという状態が続きました。
私のミスだったので、同じような状態の人に私の経験を伝えれればと思い書きます。

内容

紐付けしたオブジェクトを削除するために、dependent: :destroyをつけても、削除できない時に気をつけて欲しいことを書きます。

問題点

ある特定のUserのみ削除できない

モデルの関係性

UserCartが親子関係で、
CartProductCart_productsと 1対多 です。
Users ー Carts → Cart_products ← Products

class User < ApplicationRecord
  has_one :cart, dependent: :destroy
end

class Cart < ApplicationRecord
  belongs_to :user
  has_many :cart_products, dependent: :destroy
  has_many :products, through: :cart_products
end

class Product < ApplicationRecord
  has_many :cart_products, dependent: :destroy
end

class CartProduct < ApplicationRecord
  belongs_to :cart
  belongs_to :product
end

行っていたこと

関連付けられたオブジェクトもdestroy削除できるように
has_many,has_onedependent: :destroyをつけていた。

エラー文

Cannot delete or update a parent row: a foreign key constraint fails (`app_name_development`.`cart_products`, CONSTRAINT `fk_rails_a4f3e327f3` FOREIGN KEY (`cart_id`) REFERENCES `carts` (`id`))

原因

User has_one CartUser にはCartがは1つのみ紐づくようにしていたのですが、
削除できないUserには2つのCartが紐つかれていた。

そのため
User has_one CartUser has_many Cartsと変更すると削除できた。

+----+---------------------+---------------------+---------+
| id | created_at          | updated_at          | user_id |
+----+---------------------+---------------------+---------+
|  3 | 2020-02-21 07:07:57 | 2020-02-21 07:07:57 |       4 |
| 12 | 2020-02-21 07:29:37 | 2020-02-21 07:29:37 |       7 |
| 13 | 2020-02-21 07:34:26 | 2020-02-21 07:34:26 |       7 |
| 16 | 2020-02-22 07:51:47 | 2020-02-22 07:51:47 |      11 |
+----+---------------------+---------------------+---------+

今後の対処法

モデルを作成する時に、オプションとしてunique: trueを付け、userの重複をなしにする。

XXXXXXX_create_cart.rb
class CreateCarts < ActiveRecord::Migration[5.2]
  def change
    create_table :carts do |t|
      t.references :user, foreign_key: true, unique: true #ここ

      t.timestamps
    end
  end
end

結論

User has_one Cart 関係の
User に1つのみのCartが紐づくようにしていたのですが、
削除できないUserには2つのCartが紐つかれていた。

そのため、以下のようにUser has_one CartUser has_many Cartsと変更すると削除できた。

class User < ApplicationRecord
  has_one :cart, dependent: :destroy
end

⬇︎

class User < ApplicationRecord
  has_many :cart, dependent: :destroy
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DockerでMySQLを立ち上げる時に文字コードを指定する

docker run --restart=always -d -e MYSQL_ROOT_PASSWORD=mysql \
    -p 3306:3306 --name testdb mysql:8.0 --character-set-server=utf8mb4
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【MySQL】自動起動の意外な落とし穴

はじめに

ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)

ログインする際のエラーを解決するためにサーバーを一旦停止したかった時のことです。
自動起動設定をしたことをすっかり忘れていて、時間がかかったのでまとめておきます。

そもそもの自動設定は、どうやってた?

brewのhomebrew-servicesを使っていた。

公式サイトはこちら。
https://github.com/Homebrew/homebrew-services

使い方

MySQLを自動起動にする。

$ brew services start mysql

==> Successfully started `mysql` (label: homebrew.mxcl.mysql)

これだけ。

自動起動を止めたい時は、

$ brew services stop mysql

Stopping `mysql`... (might take a while)
==> Successfully stopped `mysql` (label: homebrew.mxcl.mysql)

一覧を取得したい。

$ brew services list

Name       Status  User   Plist
mysql      started ------ /Users/------/Library/LaunchAgents/homebrew.mxcl.mysql.plist
postgresql started ------ /Users/------/Library/LaunchAgents/homebrew.mxcl.postgresql.plist

自動起動がオンになっていると、停止コマンドは成功するが確認すると止まってない。。。

% mysql.server stop
Shutting down MySQL
. SUCCESS!

% mysql.server status
 SUCCESS! MySQL running (21754)

パッと見「サクセス!!!」って完全に停止できてると信じていたけれど、騙されました。
いざコマンドで確認したところ、普通に動いてて、意味がわからなかった。。。
そして、自動起動について書かれてる記事をみた時に、ハッと思い出して、、、
今に至ります(笑)

おわりに

便利なものをインストールして、効率を上げるのはいいことだけど、しっかり把握しておかないと今回のようなことになってしまうので注意します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む