読者です 読者をやめる 読者になる 読者になる

【Rails】ActiveRecord入門とりあえず使えるようにまとめておく@初心者

はじめに

Railsをすこしいじり始めて、DBも使いたいのでActiveRecordをいじってみる
PHPフレームワークではORMをほとんど使った事なかったので(喰わず嫌いで)、ちょっと新鮮だった。

とりあえず一般的というか複雑でないSQLは使えるようになりたいということでメモしておく

検証用のテーブル

・categories(動画のカテゴリー)

idカテゴリーIDPK
nameカテゴリー名

・videos(動画)

id動画IDPK
category_idカテゴリーIDFK
titleタイトル

Modelにリレーションを設定

app/models/video.rb

class Video < ApplicationRecord
  belongs_to :category
end

app/models/category.rb

class Category < ApplicationRecord
  has_many :videos
end

使用例

全レコード取得
> videos = Video.all()
Video Load (0.3ms)  SELECT `videos`.* FROM `videos`
ORDER BY
> videos = Video.order("id desc")
Video Load (0.3ms)  SELECT `videos`.* FROM `videos` ORDER BY id desc
OFFSET・LIMIT
> videos = Video.order("id desc").offset(0).limit(2)
Video Load (0.4ms)  SELECT  `videos`.* FROM `videos` ORDER BY id desc LIMIT 2 OFFSET 0
GROUP BYしてCOUNT
> videos = Video.group("videos.category_id").count
 (0.8ms)  SELECT COUNT(*) AS count_all, videos.category_id AS videos_category_id FROM `videos` GROUP BY videos.category_id
=> {nil=>1, 1=>2, 2=>1}
INNER JOIN

これは負荷の面を考慮するとあまり使わないほうがよいかも。。。

> videos = Video.joins(:category)
Video Load (0.4ms)  SELECT `videos`.* FROM `videos` INNER JOIN `categories` ON `categories`.`id` = `videos`.`category_id`

> videos[0]
=> #<Video id: 1, category_id: 1, title: "動画タイトル1">

> videos[0].category
Category Load (0.9ms)  SELECT  `categories`.* FROM `categories` WHERE `categories`.`id` = 1 LIMIT 1 # ★ココでもSQLが実行される
=> #<Category id: 1, name: "カテゴリー1">

> videos[0].category
=> #<Category id: 1, name: "カテゴリー1">
INNER JOIN(IN句を使って擬似的にINNER JOIN)
> videos = Video.includes(:category)
Video Load (0.7ms)  SELECT `videos`.* FROM `videos`
Category Load (0.4ms)  SELECT `categories`.* FROM `categories` WHERE `categories`.`id` IN (1, 2)

> videos[0]
=> #<Video id: 1, category_id: 1, title: "動画タイトル1">

> videos[0].category
=> #<Category id: 1, name: "カテゴリー1">
LEFT JOIN
> videos = Video.includes(:category).references(:category)
SQL (0.5ms)  SELECT `videos`.`id` AS t0_r0, `videos`.`category_id` AS t0_r1, `videos`.`title` AS t0_r2, `categories`.`id` AS t1_r0, `categories`.`name` AS t1_r1 FROM `videos` LEFT OUTER JOIN `categories` ON `ca\
tegories`.`id` = `videos`.`category_id`

> videos[0]
=> #<Video id: 1, category_id: 1, title: "動画タイトル1">

> videos[0].category
=> #<Category id: 1, name: "カテゴリー1">
INSERT
> category = new Category(name:"テスト")
> category.save
UPDATE
> category = Category.find_by_id(1)
> category.name = "テスト"
> category.save

おわりに

理解が浅いかとは思うが、inner joinをしたい場合にjoinsを単体で呼ぶのが危険な気がした。

ループ内でvideo.categoryを参照するタイミングでSQLが実行される

> videos = Video.joins(:category)
> videos.each { |video| print video.category }
Category Load (0.7ms)  SELECT  `categories`.* FROM `categories` WHERE `categories`.`id` = 1 LIMIT 1
#<Category:0x007ff6917902e0>  Category Load (0.5ms)  SELECT  `categories`.* FROM `categories` WHERE `categories`.`id` = 1 LIMIT 1
#<Category:0x007ff69179ec78>  Category Load (0.9ms)  SELECT  `categories`.* FROM `categories` WHERE `categories`.`id` = 2 LIMIT 1

まぁ、レコード数とかあらかじめわかっていれば使いどころによるのかもしれませんが。。。

以上です