728x90
[RubyOnRails Guides] Active Record Query Interface - 2ํธ
๐ผ ์๋ก
- RubyOnRails Guides Active Record Basics ๋ฅผ ์ฐธ๊ณ ํด ์์ฑํ ๊ธ์ ๋๋ค.
- Ruby version์ 2.6.3์ ์ฌ์ฉํฉ๋๋ค.
- Ruby On Rails version์ 5.2.1์ ์ฌ์ฉํฉ๋๋ค.
- 1ํธ์์๋ ๋จ๊ฑด ๋ฐ ๋ณต์ ์กฐํ, ์กฐ๊ฑด, ์ ๋ ฌ, ๊ทธ๋ฃน ๋ฑ์ ์ ๋ฆฌํ์ต๋๋ค (์ฐธ๊ณ ๋งํฌ)
Locking Records for Update
- DB ํธ๋์ญ์ ์ด 2๊ฐ ์ด์ ์งํ๋ ๊ฒฝ์ฐ ํ๋ก์ธ์ค ๋ด์์ ์์ ํ๋ ์ฐ๋ ๋ ์์์ด ๊ผฌ์ฌ race codnition(๊ฒฝ์ ์ํ) ๋ฌธ์ ๊ฐ ๋ฐ์ํด ์ฐ๋ฆฌ๊ฐ ๊ธฐ๋ํ ์ฒ๋ฆฌ๊ฒฐ๊ณผ๊ฐ ๋์ค์ง ์์ ๊ฒฝ์ฐ๊ฐ ์๋ค.
- ๋ฐ๋ผ์ ์ฐ๋ฆฌ๋ ์ด๋ฐ ๊ฒฝ์ฐ Thread Safe ํ๊ฒ ํด์ค์ผ ํ๋๋ฐ, DB์์๋ Lcok ๊ธฐ๋ฅ์ผ๋ก ๊ฐ๋ฅํ๊ฒ ํ๋ค.
- Locking์ด๋ ๋๊ฐ ์ด์์ ์์ ์ด ๋์์ ์งํ๋ ๊ฒฝ์ฐ ์ด์ ์์ ์ด ๋๋ ๋ ๊น์ง ๋๊ธฐํ๊ฒ ๋ง๋๋ ๊ฒ์ ๋๋ค. Locking์ ์ค์ ํ๋ฉด ํธ๋์ญ์ ์ด unlock ํ ๋๊น์ง ๋ฐ์ดํฐ๋ฅผ ๋ ์ ์ ์ผ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค.
- Locking์๋ ๋๊ฐ์ง ์ข ๋ฅ๊ฐ ์์ต๋๋ค. ๋๊ด์ ์ ๊ธ, ๋น๊ด์ ์ ๊ธ์ ๋๋ค.
Optimistic locking(๋๊ด์ ์ ๊ธ)
- ์ฌ๋ฌ ์ฌ์ฉ์๊ฐ ํธ์ง์ ์ํด ๋์ผํ ๋ ์ฝ๋์ ์ ๊ทผํ ์ ์์ผ๋ฉฐ ๋ฐ์ดํฐ์ ์ต์ ์ถฉ๋์ ๊ฐ์ ํฉ๋๋ค.
- ํ ์ด๋ธ์ lock_version ์ปฌ๋ผ์ด ์๊ณ ๋ ์ฝ๋๊ฐ ์ ๋ฐ์ดํธ ๋ ๋๋ง๋ค lock_version์ ์ฆ๊ฐ์ํต๋๋ค.
- ํ์ฌ lock_version ์ปฌ๋ผ ๊ฐ๋ณด๋ค ๋ ๋ฎ์ ๊ฐ์ผ๋ก ์ ๋ฐ์ดํธ๋ฅผ ํ ๊ฒฝ์ฐ ActvieRecord::StaleObejctError์ ํจ๊ป ์ ๋ฐ์ดํธ ์์ฒญ์ด ์คํจํฉ๋๋ค.
- ์ด์์๊ฐ ๋ฐฐ์ก์ํ๋ฅผ ๋ณ๊ฒฝํ๊ธฐ ์ํด version์ด 1์ธ ์ฃผ๋ฌธ ์ ๋ณด๋ฅผ ์กฐํํฉ๋๋ค.
- ๊ณ ๊ฐ์ ๋ฐฐ์ก์ง๋ฅผ ๋ณ๊ฒฝํ๊ธฐ ์ํด version์ด 1์ธ ์ฃผ๋ฌธ์ ๋ณด๋ฅผ ์กฐํํฉ๋๋ค.
- ์ด์์๋ ๋ฐฐ์ก์ํ๋ฅผ ๋ฐฐ์ก์ค์ผ๋ก ๋ณ๊ฒฝํฉ๋๋ค.
- ๊ณ ๊ฐ์ ๋ฐฐ์ก์ง๋ฅผ ๋ณ๊ฒฝํฉ๋๋ค.
- ์ด์์์ ํธ๋์ญ์ ์ด ์ปค๋ฐ๋๋ฉด์ version์ด 2๋ก ๋ณ๊ฒฝ๋ฉ๋๋ค.
- ์ดํ์ ๊ณ ๊ฐ์ ํธ๋์ญ์ ์ ์ปค๋ฐํ๋ฉด version์ด ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ ์ปค๋ฐ์ด ์คํจํ๊ฒ ๋ฉ๋๋ค.
- ์์ ๋ฉ์ปค๋์ฆ์ฒ๋ผ ํธ๋์ญ์ ์ถฉ๋์ด ๋ฐ์ํ์ง ์๋๋ก ์ฒ๋ฆฌํ๊ฒ ๋ฉ๋๋ค.
- ๋ฃจ๋น์์ ๋๊ด์ ์ ๊ธ์ ํ๊ณ , ์๋์ ๊ฐ์ด ์คํํ๋ฉด ActvieRecord::StaleObejctError๊ฐ ๋ฐ์ํ๊ฒ ๋ฉ๋๋ค.
c1 = Client.find(1)
c2 = Client.find(1)
c1.first_name = "Michael"
c1.save
c2.name = "should fail"
c2.save # Raises an ActiveRecord::StaleObjectError
- lock_version์ ์ปฌ๋ผ๋ช ์ ๋ณ๊ฒฝํ ์๋ ์์ต๋๋ค.
class Client < ApplicationRecord
lock_optimistically = true
self.locking_column = :lock_client_column
end
Pessimistic Locking(๋น๊ด์ ์ ๊ธ)
- DB์์ ์ฌ์ฉํ๋ ๊ธฐ๋ณธ locking ๋ฉ์ปค๋์ฆ์ ์ฌ์ฉํ๋ค.
- ๊ด๊ณ๋ฅผ ์์ฑํ ๋ lock์ ์ฌ์ฉํ๋ฉด ์ ํํ ํ(row)์์ exclusive lock์ ์ป์ต๋๋ค.
- ๋น๊ด์ ์ ๊ธ์ Dead Lock(๊ต์ฐฉ ์ํ)์ ๋น ์ง์ ์๋ค.
- ์ค๋ ๋ 1: A ์ ๋ณด๋ฅผ ๊ตฌํ๊ณ ์ ๊ธ
- ์ค๋ ๋ 2 : B ์ ๋ณด๋ฅผ ๊ตฌํ๊ณ ์ ๊ธ
- ์ค๋ ๋ 1: B ์ ๋ณด๋ฅผ ๊ตฌํ๊ณ ์ ํ ๋ ๋ธ๋กํน
- ์ค๋ ๋ 2: A ์ ๋ณด๋ฅผ ๊ตฌํ๊ณ ์ ํ ๋ ๋ธ๋กํน
- ๊ต์ฐฉ ์ํ๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด lock์ ์ฌ์ฉํ๋ ๊ด๊ณ๋ ์ผ๋ฐ์ ์ผ๋ก ํธ๋์ญ์ ๋ด๋ถ์ ๋ฉํ(warpped) ๋ฉ๋๋ค.
Item.transaction do
i = Item.lock.first
i.name = 'Jones'
i.save!
end
SQL (0.2ms) BEGIN
Item Load (0.3ms) SELECT * FROM `items` LIMIT 1 FOR UPDATE
Item Update (0.4ms) UPDATE `items` SET `updated_at` = '2009-02-07 18:05:56', `name` = 'Jones' WHERE `id` = 1
SQL (0.8ms) COMMIT
- lock์ ๋ค๋ฅธ ์ ํ์ ํ์ฉํ๊ธฐ ์ํด ์์ SQL์ ๋ฉ์๋์ ์ ๋ฌํ ์ ๋ ์์ต๋๋ค.
Item.transaction do
i = Item.lock("LOCK IN SHARE MODE").find(1)
i.increment!(:views)
end
- ๋ชจ๋ธ ์ธ์คํด์ค๊ฐ ์ด๋ฏธ ์๋ ๊ฒฝ์ฐ ์๋์ ๊ฐ์ ์ฝ๋๋ก ํธ๋์ญ์ ์ ์์ํ๊ณ ์ ๊ธ์ ํ ์ ์์ต๋๋ค.
item = Item.first
item.with_lock do
# This block is called within a transaction,
# item is already locked.
item.increment!(:views)
end
Joining Tables
joins
- ์กฐ์ธ์ ์ข ๋ฅ๋ ์ฌ๋ฌ๊ฐ์ง๊ฐ ์์ต๋๋ค.
ํน์ JOIN ์ ์ ์ง์ ํด SQL์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
Author.joins("INNER JOIN posts ON posts.author_id = authors.id AND posts.published = 't'") SELECT authors.* FROM authors INNER JOIN posts ON posts.author_id = authors.id AND posts.published = 't'
Assciation ์ด๋ฆ์ ์กฐ์ธ ๋ฐฉ๋ฒ์ผ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค.
class Category < ApplicationRecord has_many :articles end class Article < ApplicationRecord belongs_to :category has_many :comments has_many :tags end class Comment < ApplicationRecord belongs_to :article has_one :guest end class Guest < ApplicationRecord belongs_to :comment end class Tag < ApplicationRecord belongs_to :article end
- ์์์ INNER JOIN์ด ๋ฉ๋๋ค.
๋จ์ผ ์ฐ๊ฒฐ ์กฐ์ธ ํ๋ ๋ฐฉ๋ฒ
Category.joins(:articles) SELECT categories.* FROM categories INNER JOIN articles ON articles.category_id = categories.id
1:N์ ๊ด๊ณ์ด๋ฉด ์ค๋ณต cateogoies๊ฐ ์๋๋ฐ disinct ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค.
Category.joins(:articles).distinct
๋ค์ค ์ฐ๊ฒฐ์กฐ์ธํ๋ ๋ฐฉ๋ฒ
Article.joins(:category, :comments) SELECT articles.* FROM articles INNER JOIN categories ON categories.id = articles.category_id INNER JOIN comments ON comments.article_id = articles.id
Category.joins(articles: [{ comments: :guest }, :tags]) SELECT categories.* FROM categories INNER JOIN articles ON articles.category_id = categories.id INNER JOIN comments ON comments.article_id = articles.id INNER JOIN guests ON guests.comment_id = comments.id INNER JOIN tags ON tags.article_id = articles.id
- Join Table์ ์กฐ๊ฑด์ ์ง์ ํ ์๋ ์์ต๋๋ค.
time_range = (Time.now.midnight - 1.day)..Time.now.midnight
Client.joins(:orders).where('orders.created_at' => time_range)
- ์์ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํ๋ฉด BETWEEN๋ฌธ์ ์กฐ๊ฑด์ด ํฌํจ๋๊ฒ ๋ฉ๋๋ค.
left_outer_joins
- OuterJoin์ ํ๋ ๋ฐฉ๋ฒ์ ๋๋ค.
Author.left_outer_joins(:posts).distinct.select('authors.*, COUNT(posts.*) AS posts_count').group('authors.id')
SELECT DISTINCT authors.*, COUNT(posts.*) AS posts_count FROM "authors"
LEFT OUTER JOIN posts ON posts.author_id = authors.id GROUP BY authors.id
Eager Loding
- ์ฆ์๋ก๋ฉ์ผ๋ก Model.find() ์ ์ฐ๊ด๊ด๊ณ๋ก ๋์ด ์๋ ์ฝ๋๋ฅผ ํ๋ฐฉ์ ๋ถ๋ฌ์ค๋ ๊ฒ ์ ๋๋ค.
N + 1 ์ฟผ๋ฆฌ ๋ฌธ์
clients = Client.limit(10)
clients.each do |client|
puts client.address.postcode
end
- 10๋ช ์ ํด๋ผ์ด์ธํธ๋ฅผ ์กฐํํ ์ดํ์ ์ฃผ์๋ฅผ ๋ถ๋ฌ์ค๋ฉด 10๊ฐ์ ํด๋ผ์ด์ธํธ๋ฅผ ๋ถ๋ฌ์ค๋ SQLํ๋์ ๊ฐ ์ฃผ์๋ฅผ ๋ถ๋ฌ์ค๋ SQL 10๊ฐ๋ก N + 1 ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ฒ ๋ฉ๋๋ค.
N + 1 ํด๊ฒฐ ๋ฐฉ๋ฒ
- ์ด๊ฑธ ํด๊ฒฐ ํ๊ธฐ ์ํด eager๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค.
- Lazy Loding์ด ์๋๋ผ Eager Loding์ผ๋ก Clinet๋ฅผ ์กฐํํ ๋ join์ผ๋ก address๊น์ง ๊ฐ์ด ๋ถ๋ฌ์ค๊ฒ ๋ฉ๋๋ค.
clients = Client.includes(:address).limit(10)
clients.each do |client|
puts client.address.postcode
end
SELECT * FROM clients LIMIT 10
SELECT addresses.* FROM addresses
WHERE (addresses.client_id IN (1,2,3,4,5,6,7,8,9,10))
- ์๋์ ๊ฐ์ด ์กฐ๊ฑด์ ์ง์ ํ ์๋ ์์ต๋๋ค.
Article.includes(:comments).where(comments: { visible: true })
SELECT "articles"."id" AS t0_r0, ... "comments"."updated_at" AS t1_r5 FROM "articles" LEFT OUTER JOIN "comments" ON "comments"."article_id" = "articles"."id" WHERE (comments.visible = 1)
range
- ๋ฒ์๋ฅผ ์ง์ ํ๋ฉด ์ฐ๊ด ๊ด๊ณ ๋ชจ๋ธ์ ๋ํ ๋ฉ์๋ ํธ์ถ๋ก ์ฐธ์กฐ ๋ ์ ์๋ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉ๋๋ ์ฟผ๋ฆฌ๋ฅผ ์ง์ ํ ์ ์์ต๋๋ค.
- scope๋ ์๋์ ๊ฐ์ด ์ค์ ํ ์ ์์ต๋๋ค.
class Article < ApplicationRecord
scope :published, -> { where(published: true) }
end
class Article < ApplicationRecord
def self.published
where(published: true)
end
end
- 2๊ฐ๋ค ๋ชจ๋ ๋์ผํ๋ฉฐ ์ํ์๋ ๋ฐฉ์์ผ๋ก ์ค์ ํ๋ฉด ๋ฉ๋๋ค.
- range ๋ด์์๋ ์ฐ๊ฒฐ์ด ๊ฐ๋ฅํ๋น๋ค.
class Article < ApplicationRecord
scope :published, -> { where(published: true) }
scope :published_and_commented, -> { published.where("comments_count > 0") }
end
- published ๋ฒ์๋ฅผ ํธ์ถํ๊ธฐ ์ํด์๋ published ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค.
Article.published
๋งค๊ฐ๋ณ์ ์ ๋ฌ
- ๋งค๊ฐ๋ณ์๋ฅผ ์ ๋ฌํ ์ ๋ ์์ต๋๋ค.
class Article < ApplicationRecord
scope :created_before, ->(time) { where("created_at < ?", time) }
end
class Article < ApplicationRecord
def self.created_before(time)
where("created_at < ?", time)
end
end
- ์๋์ ๊ฐ์ด ํธ์ถ์ด ๊ฐ๋ฅํฉ๋๋ค.
Article.created_before(Time.zone.now)
์กฐ๊ฑด๋ฌธ ์ฌ์ฉ
class Article < ApplicationRecord
scope :created_before, ->(time) { where("created_at < ?", time) if time.present? }
end
class Article < ApplicationRecord
def self.created_before(time)
where("created_at < ?", time) if time.present?
end
end
- ํ๊ฐ์ง ์ฃผ์์ฌํญ์ ์กฐ๊ฑด์ false๊ฐ ๋์ค๋ฉด nul์ ๋ฐํํ์ง๋ง, scope๋ ํญ์ ActiveRecord::Relation ๊ฐ์ฒด๋ฅผ ๋ฐํํฉ๋๋ค. ์ฆ, ์กฐ๊ฑด ์ค ํ๋๋ผ๋ false๋ฅผ ๋ฐํํ๋ฉด ์กฐ๊ฑด๋ถ์ class method๋ฅผ ์ฐ๊ฒฐํ ๋ NoMethodError๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
default scope(๊ธฐ๋ณธ ๋ฒ์)
- ์๋์ ๊ฐ์ด ๊ธฐ๋ณธ ๋ฒ์๋ฅผ ์ง์ ํ ์ ์์ต๋๋ค.
class Client < ApplicationRecord
default_scope { where("removed_at IS NULL") }
end
class Client < ApplicationRecord
def self.default_scope
# Should return an ActiveRecord::Relation.
end
end
- ์ฃผ์์ฌํญ์ Query Arguments๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
class Client < ApplicationRecord
default_scope { where("active = ?", true) }
end
Client.new # => #<Client id: nil, active: nil>
scope ๋ณํฉ
class User < ApplicationRecord
scope :active, -> { where state: 'active' }
scope :inactive, -> { where state: 'inactive' }
end
User.active.inactive
SELECT "users".* FROM "users" WHERE "users"."state" = 'active' AND "users"."state" = 'inactive'
- AND ์กฐ๊ฑด์ ์ฌ์ฉํด scope๋ฅผ ํฉ์น๋ ๊ฒ์ ๋๋ค.
- ์ฆ, ์ต์ข SQL์ด AND์ JOIN ๋ ๋ชจ๋ ์กฐ๊ฑด์ ๊ฐ๋๋ก scope๋ฅผ ํผํฉํ๊ณ ์ผ์น์ํฌ ์ ์์ต๋๋ค.
User.active.merge(User.inactive)
SELECT "users".* FROM "users" WHERE "users"."state" = 'inactive'
class User < ApplicationRecord
default_scope { where state: 'pending' }
scope :active, -> { where state: 'active' }
scope :inactive, -> { where state: 'inactive' }
end
User.all
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending'
User.active
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' AND "users"."state" = 'active'
User.where(state: 'inactive')
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' AND "users"."state" = 'inactive'
- ์์ ๊ฐ์ด ์ค์ํ ์ฃผ์ ์ฌํญ์ด ์์ต๋๋ค. default_scope๊ฐ ์ ์ผ ๋จผ์ ์คํ๋ฉ๋๋ค.(์ฆ, scope, where ์์ ์ถ๊ฐ)
scope ์ญ์
- unscope ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ์ญ์ ํ ์ ์์ต๋๋ค.
Client.unscoped.load
Client.unscoped.all
# SELECT "clients".* FROM "clients"
Client.where(published: false).unscoped.all
# SELECT "clients".* FROM "clients"
- unscope๋ ์ฌ์ ์ ์ ์๋ ๋ชจ๋ scope๋ฅผ ์ ๊ฑฐํ๊ณ ํ ์ด๋ธ์์๋ ์ผ๋ฐ Query๋ก ์์ ํ ์ ์๋ค๊ณ ์๊ฐํ๋ฉด ์ข์ต๋๋ค.
- ๋ธ๋ก์ ํ์ฉ๋ ํฉ๋๋ค.
Client.unscoped {
Client.created_before(Time.zone.now)
}
Dynamic Finders
- ํ
์ด๋ธ์ ์ ์ํ ๋ชจ๋ ํ๋์ ๋ํด finder๋ฅผ ์ ๊ณตํฉ๋๋ค. ์๋ฅผ๋ค์ด find_by_name๋ผ๋ ๋ฉ์๋๋ก
SELECT * FROM USER WHERE name =
์ฟผ๋ฆฌ๋ฅผ ๋ง๋ค์ ์๋ค. - ์ฌ๋ฌ๊ฐ๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด and๋ฅผ ์ด์ฉํ๋ฉด ๋๋ค.
- Client.find_by_first_name_and_locked(...)
enum
- ์ ์ ๊ฐ์ Set์ผ๋ก ๋ชจ์ ๋๋ enum๋ ์ฌ์ฉ์ด ๊ฐ๋ฅํฉ๋๋ค.
class Book < ApplicationRecord
enum availability: [:available, :unavailable]
end
# Both examples below query just available books.
Book.available
# or
Book.where(availability: :available)
book = Book.new(availability: :available)
book.available? # => true
book.unavailable! # => true
book.available? # => false
Method chaining
- ์ฌ๋ฌ ํ ์ด๋ธ์์ ํํฐ๋ง ๋ ๋ฐ์ดํฐ ๊ฒ์
Person
.select('people.id, people.name, comments.text')
.joins(:comments)
.where('comments.created_at > ?', 1.week.ago)
SELECT people.id, people.name, comments.text
FROM people
INNER JOIN comments
ON comments.person_id = people.id
WHERE comments.created_at = '2015-01-01'
- ์ฌ๋ฌ ํ ์ด๋ธ์์ ํน์ ๋ฐ์ดํฐ ๊ฒ์
Person
.select('people.id, people.name, companies.name')
.joins(:company)
.find_by('people.name' => 'John') # this should be the last
SELECT people.id, people.name, companies.name
FROM people
INNER JOIN companies
ON companies.person_id = people.id
WHERE people.name = 'John'
LIMIT 1
์๋ก์ด ๋ชจ๋ธ ์กฐํ ๋๋ ์ ์ฅ
find_or_create_by
- ๋ ์ฝ๋๊ฐ ์กด์ฌํ๋ฉด ์กฐํํ๊ณ ๋ ์ฝ๋๊ฐ ์์ผ๋ฉด ํด๋น ๋ ์ฝ๋๋ฅผ ์ ์ฅํฉ๋๋ค.
Client.find_or_create_by(first_name: 'Andy')
SELECT * FROM clients WHERE (clients.first_name = 'Andy') LIMIT 1
BEGIN
INSERT INTO clients (created_at, first_name, locked, orders_count, updated_at) VALUES ('2011-08-30 05:22:57', 'Andy', 1, NULL, '2011-08-30 05:22:57')
COMMIT
find_or_create_by!
- create๊ฐ ์๋ new ๋ฉ์๋๋ก ๋ฐ์ดํฐ๊ฐ ์ด๊ธฐํ ๋ฉ๋๋ค. ์ฆ, ์๋ก์ด Modal ์ธ์คํด์ค๊ฐ ์์ฑ๋์ง๋ง, DB์๋ ์ ์ฅ๋์ง ์์ต๋๋ค.
Client.find_or_create_by!(first_name: 'Marco')
- validate์ค์ presence: true๋ก ์ค์ ๋์ด ์๋๊ฒ ์๋ค๋ฉด, ์์ธ๊ฐ ๋ฐ์ํฉ๋๋ค.
find_or_initialize_by
- DB์ ์ ์ฅ๋์ง ์๊ณ ์ธ์คํด์ค๋ง ์์ฑํฉ๋๋ค.
- ์ถํ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ ์ถ๋ค๋ฉด save ๋ฉ์๋๋ฅผ ์คํ์์ผ์ฃผ๋ฉด ๋ฉ๋๋ค.
๋ชจ๋ธ ์กฐํ ์ฟผ๋ฆฌ
find_by_sql
- SQL์ ์ด์ฉํด ํ ์ด๋ธ์์ ๋ฐ์ดํฐ๋ฅผ ์กฐํํฉ๋๋ค.
Client.find_by_sql("SELECT * FROM clients
INNER JOIN orders ON clients.id = orders.client_id
ORDER BY clients.created_at desc")
# => [
# #<Client id: 1, first_name: "Lucas" >,
# #<Client id: 2, first_name: "Jan" >,
# ...
# ]
select_all
- ๋ชจ๋ ๋ชจ๋ธ์ ๊ฐ์ ธ์ค๋ ์กฐํ ์ฟผ๋ฆฌ์ ๋๋ค.
Client.connection.select_all("SELECT first_name, created_at FROM clients WHERE id = '1'").to_a
# => [
# {"first_name"=>"Rafael", "created_at"=>"2012-11-10 23:23:45.281189"},
# {"first_name"=>"Eileen", "created_at"=>"2013-12-09 11:22:35.221282"}
# ]
pluck
- ๊ธฐ๋ณธ ํ ์ด๋ธ์์ ๋จ์ผ ๋๋ ์ฌ๋ฌ ์ปฌ๋ผ์ ๊ฒ์ํ๋๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
Client.where(active: true).pluck(:id)
# SELECT id FROM clients WHERE active = 1
# => [1, 2, 3]
Client.distinct.pluck(:role)
# SELECT DISTINCT role FROM clients
# => ['admin', 'member', 'guest']
Client.pluck(:id, :name)
# SELECT clients.id, clients.name FROM clients
# => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
- ๊ฒฐ๊ณผ๊ฐ ๋ฐฐ์ด๋ก ๋์ค๊ธฐ ๋๋ฌธ์ ์ฟผ๋ฆฌ ์ฑ๋ฅ์ ํฅ์์ํฌ์ ์์ต๋๋ค.
- ๊ทธ๋ฌ๋ pluck๋ฅผ ์คํ์ํค๋ ์๊ฐ Query๋ฅผ triggerํ๊ธฐ ๋๋ฌธ์ chaining์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
Client.pluck(:name).limit(1)
# => NoMethodError: undefined method `limit' for #<Array:0x007ff34d3ad6d8>
Client.limit(1).pluck(:name)
# => ["David"]
ids
- ๊ธฐ๋ณธํค๋ฅผ ์ฌ์ฉํด ๊ด๊ณ์ ๋ชจ๋ ID๋ฅผ ๊ฐ์ ธ์ฌ์ ์์ต๋๋ค.
๋ชจ๋ธ ์กด์ฌ ์ฌ๋ถ ์ฟผ๋ฆฌ
exist?
- ๋จ์ํ ๊ฐ์ฒด์ ์กด์ฌ๋ฅผ ํ์ธํ๋ ๊ฒ์ผ๋ก Boolean๊ฐ์ด ๋์จ๋ค.
- ์ฌ๋ฌ ๋ฐ์ดํฐ์ ๋ํ ๊ฐ๋ ๊ฐ๋ฅํฉ๋๋ค.
Client.exists?(id: [1,2,3])
# or
Client.exists?(name: ['John', 'Sergei'])
- ๊ทธ๋ฌ๋ ์์ ๋ฐฉ์์ ์ผ๋ถ๋ง ์กด์ฌํด๋ true๋ฅผ ๋ฐํํ๋ฉฐ ๋ชจ๋ ์กด์ฌํ์ง ์์์ผ false๋ฅผ ๋ฐํํฉ๋๋ค.
์ปฌ๋ผ ๊ฐ ๊ณ์ฐ ์ฟผ๋ฆฌ
Count
- ๋ชจ๋ธ ๊ฐฏ์๋ฅผ ์ ์ ์์ต๋๋ค.
Bulletin.count
# (0.1ms) SELECT COUNT(*) FROM "bulletins"
# => 63
Average & Sum
- ํ๊ท & ํฉ๊ณ์ ๊ณ์ฐํ ์ ์์ต๋๋ค.
Client.average("orders_count")
Client.sum("orders_count")
Maximum & Minimum
- ์ต๋๊ฐ& ์ต์๊ฐ์ ์กฐํํ ์ ์์ต๋๋ค.
Client.maximum("age")
Client.minimum("age")
์ฟผ๋ฆฌ ํ์ ๊ฒฐ๊ณผ ํ์ธ
- ์ฟผ๋ฆฌ ํ์ ๊ณผ์ ์ ์ดํด๋ณผ ์ ์๋
explain
๋ฉ์๋๊ฐ ์กด์ฌํฉ๋๋ค.
User.where(id: 1).joins(:articles).explain
## PostgreSQL
EXPLAIN for: SELECT "users".* FROM "users" INNER JOIN "articles" ON "articles"."user_id" = "users"."id" WHERE "users"."id" = 1
QUERY PLAN
------------------------------------------------------------------------------
Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
Join Filter: (articles.user_id = users.id)
-> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
Index Cond: (id = 1)
-> Seq Scan on articles (cost=0.00..28.88 rows=8 width=4)
Filter: (articles.user_id = 1)
(6 rows)
๐ ๊ฒฐ๋ก
- Rails๋ Active Record๋ก ORM ๊ธฐ๋ฅ์ ์ง์ํฉ๋๋ค.
- Query Interface๋ก ๊ฐ๋ฐ์๊ฐ Query๋ฅผ ์์ฑํ์ง ์๊ณ ๋ DB SQL์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
- ๋๋ถ๋ถ์ Query ๋ช ๋ น๋ฌธ์ ์ง์ํ๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฐ์๊ฐ Query์ ์ง์คํ ํ์๊ฐ ์์ผ๋ฉฐ vender์ ๋ง์ถฐ SQL์ด ์กฐ๊ธ์ฉ ๋ค๋ฅธ๋ฐ ์ ์ฝ๊ฒ vender๋ฅผ ๋ณ๊ฒฝํ ์ ์๋ค๋ ์ฅ์ ๋ ๊ฐ์ง๊ณ ์์ต๋๋ค.
- 2ํธ์์๋ Lock, Join, Eger Loading ๋ฑ์ ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
์ถ์ฒ
728x90
728x90
'Backend > RubyOnRails' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Methods in Rails modules (0) | 2022.02.06 |
---|---|
How Rails Sessions Work(์ด๋ป๊ฒ ๋ ์ผ์ฆ ์ธ์ ์ ๋์ํ๋๊ฐ) (0) | 2021.12.24 |
[RubyOnRails Guides] Active Record Query Interface - 1ํธ (0) | 2021.03.09 |
[RubyOnRails Guides] Active Record Basics (0) | 2021.02.17 |
RubyOnRails Getting Start(Blog ๋ง๋ค๊ธฐ) 4ํธ(๋ง์ง๋ง๐จโ๐จ) (0) | 2021.02.16 |