728x90
[RubyOnRails Guides] Active Record Query Interface - 1ํธ
๐ผ ์๋ก
- RubyOnRails Guides Active Record Basics ๋ฅผ ์ฐธ๊ณ ํด ์์ฑํ ๊ธ์ ๋๋ค.
- Ruby version์ 2.6.3์ ์ฌ์ฉํฉ๋๋ค.
- Ruby On Rails version์ 5.2.1์ ์ฌ์ฉํฉ๋๋ค.
๐ฎ Active Record?
[RubyOnRails Guides] Active Record Basics
๐ DB Model ์กฐํ(SELECT)
- ๊ธฐ๋ณธ์ ์ธ Model.find(options)์ ํน์ง์ ์๋์ ๊ฐ์ต๋๋ค.
- ์ ๊ณต๋ ์ต์ ์ ๋๋ฑํ SQL๋ก ๋ณํ
- SQL ์ฟผ๋ฆฌ๋ฅผ ์์ํ๊ณ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ํด๋น ๊ฒฐ๊ณผ๋ฅผ ๊ฒ์
- ๋ชจ๋ ๊ฒฐ๊ณผ ํ์ ๋ํ ์ ์ ํ ๋ชจ๋ธ์ Ruby Instanceํ
- after_find๋ฅผ ์คํํ ํ after_initialize ์ฝ๋ฒก์ ์คํํ ์ ์์ต๋๋ค.
๐ DB Model ๋จ๊ฑด ๋ฐ ์ฌ๋ฌ๊ฑด ์กฐํ
find
- ๊ธฐ๋ณธํค(PK) ๊ธฐ๋ฐ์ id ๊ฐ์ผ๋ก ์กฐํํ๋ ๋ฉ์๋ ์ ๋๋ค.
User.find(1)
SELECT * FROM users WHERE (users.id = 1) LIMIT 1
- ํด๋น Model์ ์กฐํํ์ง ๋ชปํ ๊ฒฝ์ฐ ActiveRecord::RecordNotFound ์์ธ๋ฅผ ๋ฐ์์ํจ๋ค.
- ๊ธฐ๋ณธํค ๋ฐฐ์ด์ ์ ๊ณตํ๋ฉด ์ฌ๋ฌ๊ฐ์ Model์ ์กฐํํ ์ ์์ต๋๋ค.
User.find([1, 10])
SELECT * FROM users WHERE (users.id IN (1, 10))
take
- ์์์์ด ๋ ์ฝ๋๋ฅผ ๊ฒ์ํฉ๋๋ค.
User.take
SELECT * FROM users LIMIT 1
- ๋ฐ์ดํฐ๊ฐ ์๊ณ ์์ธ๊ฐ ๋ฐ์ํ์ง ์์ผ๋ฉด take ๋ฉ์๋๋ ni(NULL)l์ด ๋ฐํ๋ฉ๋๋ค.
- ๋งค๊ฐ๋ณ์๋ฅผ ํตํด ์ฌ๋ฌ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ์กฐํํ ์ ์์ต๋๋ค.
User.take(2)
SELECT * FROM users LIMIT 2
first
- ๊ธฐ๋ณธํค๋ก ์ ๋ ฌ๋ ์ฒซ๋ฒ์งธ ๋ฐ์ดํฐ๋ฅผ ์ฐพ์ต๋๋ค.
User.frist
SELECT * FROM users ORDER BY users.id ASC LIMIT 1
- ๋ ์ฝ๋๋ฅผ ์ฐพ์ ์ ์์ผ๋ฉด ์์ธ๊ฐ ์๋ nil(NULL)์ ๋ฐํํฉ๋๋ค.
- ๋งค๊ฐ๋ณ์๋ฅผ ์ ๊ณตํด ์ฌ๋ฌ ๊ฐ์ Model์ ๋ฐํํ ์ ์์ต๋๋ค.
User.first(3)
SELECT * FROM clients ORDER BY users.id ASC LIMIT 3
- order ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด ํด๋น ํ๋๋ก ์ ๋ ฌ ํ ์ฒซ๋ฒ์จฐ ๊ฐ์ ๋ฐํํ๊ฒ ๋ฉ๋๋ค.
User.order(:name).first
SELECT * FROM users ORDER BY users.name ASC LIMIT 1
last
- ๊ธฐ๋ณธํค๋ก ์ ๋ ฌ๋ ๋ง์ง๋ง ๋ฐ์ดํฐ๋ฅผ ์ฐพ์ต๋๋ค.
User.last
SELECT * FROM users ORDER BY users.id DESC LIMIT 1
- ๋ ์ฝ๋๋ฅผ ์ฐพ์ ์ ์์ผ๋ฉด ์์ธ๊ฐ ์๋ nil(NULL)์ ๋ฐํํฉ๋๋ค.
- ๋งค๊ฐ๋ณ์๋ฅผ ์ ๊ณตํด ์ฌ๋ฌ ๊ฐ์ Model์ ๋ฐํํ ์ ์์ต๋๋ค.
User.last(3)
SELECT * FROM clients ORDER BY users.id DESC LIMIT 3
- order ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด ํด๋น ํ๋๋ก ์ ๋ ฌ ํ ๋ง์ง๋ง ๊ฐ์ ๋ฐํํ๊ฒ ๋ฉ๋๋ค.
User.order(:name).last
SELECT * FROM users ORDER BY users.name DESC LIMIT 1
find_by
- ์ผ๋ถ ์กฐ๊ฑด๊ณผ ์ผ์นํ๋ ์ฒซ ๋ฒ์งธ ๋ ์ฝ๋๋ฅผ ๋ฐํํฉ๋๋ค.
User.find_by first_name: 'rutgo'
User.where(first_name: 'rutgo').take
SELECT * FROM users WHERE (users.first_name = 'rutgo') LIMIT 1
- ์ผ์นํ๋ ๋ ์ฝ๋๊ฐ ์์ ๊ฒฝ์ฐ์๋
ActiveRecord:RecordNotFound
์๋ฌ๊ฐ ๋ฐ์
all
- ๋ชจ๋ ๋ ์ฝ๋๋ฅผ ๊ฐ์ ธ์ค๋ ๋ฉ์๋๋ก ๋ง์ ์์ ๋ ์ฝ๋๊ฐ ์์ ๊ฒฝ์ฐ ์ ์ฒด ์ปฌ๋ ์ ์ด ์ฌ์ฉ ๊ฐ๋ฅํ ๋ฉ๋ชจ๋ฆฌ ์์ ์ด๊ณผํ ์ ์์ต๋๋ค.
- Rails๋ ๋ฐฐ์น๋ก ๋ถํ ํ์ฌ ์ฒ๋ฆฌํ๋๋ก ํ๋๋ฐ ์๋์์ ์ดํด๋ณผ
find_each
์:batch_size
์ ๋๋ค.
find_each
- Model์ด ๊ฐ์ง ๋ฐ์ดํฐ๋ฅผ ํ์ ํ์ ๊ฐ ๋ ์ฝ๋์ ๊ฐ์ฒด ๋ฐ์ดํฐ ๋ธ๋ก ๋ด์ ๊ฐ๋ณ์ ์ผ๋ก ์์ฑํฉ๋๋ค.
- ์ผ๊ด์ ํ์ ํ record๋ฅผ block์ ์์ฑํฉ๋๋ค.
User.find_each do |user|
NewsMailer.weekly(user).deliver_now
end
- ๊ธฐ๋ณธ์ ์ผ๋ก ์ฌ์ด์ฆ๋ 1000์ผ๋ก ์ค์ ๋์ด ์์ต๋๋ค.
- ์ต์ ์ ํตํด ์ค์ ํ ์๋ ์์ต๋๋ค.
- :batch_size
- Record ์ ์กฐ์
- :start
- ๋ฐ์ดํฐ ํ์ ์์
- :finish
- ๋ฐ์ดํฐ ํ์ ์ข ๋ฃ ์ง์
- :error_on_ignore
- ๊ด๊ณ์ order๊ฐ ์์ ๊ฒฝ์ฐ ์ค๋ฅ ๋ฐ์ ์ฌ๋ถ๋ฅผ ์ง์ ํ๊ธฐ ์ํด ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฌ๊ตฌ์ฑ ํฉ๋๋ค.
find_in_batches
- ๊ฐ๋ณ์ ์ด ์๋๋ผ collection ํํ๋ก Model์ ๋ฐฐ์ด๋ก block์ Batch๋ฅผ ์์ฑํฉ๋๋ค.
Invoice.find_in_fatch do |invoices|
export.add_invoices(invoices)
end
- ์ต์
์
find_each
์ ๊ฐ์ต๋๋ค.
๐น ์กฐ๊ฑด
where
- SQL WHERE์ ๊ณผ ๋์ผํ๊ฒ ์ฌ์ฉํฉ๋๋ค.
Client.where("orders_count = '2'")
SELECT * FROM clients WHERE clients.orders_count = 2
์ฃผ์ํด์ผํ ์ ์ SQL Injection ๊ณต๊ฒฉ์ ์ทจ์ฝํ ์ ์์ต๋๋ค.
๋ฐ๋ผ์ ์๋์ ๊ฐ์ด
prepared Statement์ parameterized query
๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค๋ฐฐ์ด๋ ์กฐ๊ฑด์ ์ค ์ ์์ต๋๋ค. ๋ํ ์ฌ๋ฌ๊ฐ๋ฅผ ๋ฐฐ์ด ใ ๋์๋ก ์ค ์ ์์ต๋๋ค.
Client.where("orders_count = ?", params[:orders])
Client.where("orders_count = ? AND locked = ?", params[:orders], false)
- ๋ํ key/value hash์ ํจ๊ป ํค๋ฅผ ์ง์ ํ ์๋ ์์ต๋๋ค.
Client.where("created_at >= :start_date AND created_at <= :end_date", {start_date: params[:start_date], end_date: params[:end_date]})
- ๊ฐ๋ ์ฑ์ ๋์ด๋๋ก Hash Condtion๋ฅผ ์ฌ์ฉํ ์๋ ์์ต๋๋ค.
Client.where(locked: true)
SELECT * FROM clients WHERE (clients.locked = 1)
Client.where('locked' => true)
- ์ถ๊ฐ์ ์ผ๋ก belons_to ๊ด๊ณ์ผ ๊ฒฝ์ฐ ์ฐ๊ดํค๋ฅผ ์ด์ฉํด join๋ ๊ฐ๋ฅํฉ๋๋ค.
Article.where(author: author)
Author.joins(:articles).where(articles: { author: author })
- ๋ฒ์ ์กฐ๊ฑด์ธ
BETWEEN ~ AND
๋ ๊ฐ๋ฅํฉ๋๋ค.
Client.where(created_at: (Time.mow.midnight - 1.day)..Time.now.midnight)
SELECT * FROM clients WHERE (clients.created_at BETWEEN '2008-12-21 00:00:00' AND '2008-12-22 00:00:00')
- ๋ํ IN ํํ์๋ ์ฌ์ฉ์ด ๊ฐ๋ฅํฉ๋๋ค.
Client.where(orders_count: [1,3,5])
SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5))
- NOT ํํ์๋ ๊ฐ๋ฅํฉ๋๋ค.
Client.where.not(locked: true)
SELECT * FROM clients WHERE (clients.locked != 1)
- OR ํํ์๋ ๊ฐ๋ฅํฉ๋๋ค.
Client.where(locked: true).or(Client.where(orders_count: [1,3,5]))
SELECT * FROM clients WHERE (clients.locked = 1 OR clients.orders_count IN (1,3,5))
order
- ์ ๋ ฌ์ ํด์ฃผ๋ order ํํ์์ ๋ํ ์ค๋ช ์ ๋๋ค.
- default๋ ์ค๋ฆ์ฐจ์์ผ๋ก ์ ๋ ฌ์ด ๋ฉ๋๋ค.
Client.order(:created_at) # ์ค๋ฆ์ฐจ์
Client.order(created_at: :desc) # ์ค๋ฆ์ฐจ์ ๋ช
์
Client.order(created_at: :asc) # ๋ด๋ฆผ์ฐจ์
Client.order(orders:count: :asc, created_at :desc) #์ฌ๋ฌ ํ๋๋ ์ ๋ ฌ์ด ๊ฐ๋ฅํฉ๋๋ค.
Client.order("orders_count ASC").order("created_at DESC") # ์ฌ๋ฌ๋ฒ ํธ์ถ ๋์ด๋ ํ๋์ ORDER BY๋ฌธ์ผ๋ก ๋ฌถ๊ธฐ๊ฒ ๋ฉ๋๋ค.
SELECT * FROM clients ORDER BY orders_count ASC, created_at DESC
๐ ํน์ ํ๋๋ง ์ ํ
- find ๋ฉ์๋๋ SELECT *๊ณผ ๊ฐ์ด ๋ชจ๋ ํ๋๋ฅผ ์ ํํฉ๋๋ค.
- ํน์ ํ๋๋ง ์ ํํ๊ธฐ ์ํด์๋ select ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค.
Client.select("viewable_by, locked")
SELECT viewable_by, locked FROM clients
Client.seelect(:name).distinct # ์ค๋ณต ์ ๊ฑฐ
SELECT DISTINCT name FROM clients
- ๊ณ ์ ์ฑ ์ ํ ์กฐ๊ฑด์ ์ ๊ฑฐํ ์๋ ์์ต๋๋ค.
query = Client.select(:name).distinct
query.distinct(false)
โ๏ธ Limit & Offset
Clinet.limit(5).offset
SELECT * FROM clients LIMIT 5 OFFSET 30
๐ณ Group
Order.select("date(created_at as ordered_date, sum(price) as total_price").group("date(created_at)")
SELECT date(created_at) as ordered_date, sum(price) as total_price
FROM orders
GOUP BY date(created_at)
- ๋จ์ผ ์ฟผ๋ฆฌ๋ก ๊ทธ๋ฃนํ ๋ ์ด ๊ฐฏ์๋ ์กฐํํ ์ ์์ต๋๋ค.
Order.group(:status).count
SELECT COUNT (*) AS count_all, status AS status
FROM "orders"
GROUP BY status
Having
- HAVING ์ ๋ ์ฌ์ฉ๊ฐ๋ฅํฉ๋๋ค.
Order.select("date(created_at) as ordered_date, sum(price) as total_price").group("date(created)at)").having("sum(price) > ?", 100)
SELECT COUNT (*) AS count_all, status AS status
FROM "orders"
GROUP BY status
๐ซ Condition Ignore(์กฐ๊ฑด ๋ฌด์)
unscope
- ์ฌ์ฉํ์ง ์์ ๋ฉ์๋๋ฅผ ์ง์ ํ ์ ์์ต๋๋ค. ์ง๊ธ๊ฐ์ ๊ฒฝ์ฐ์ ORDER BY๊ฐ ์ฌ์ฉํ์ง ์๋๊ฑธ ๋ณผ์ ์์ต๋๋ค.
Article.where('id > 10').limit(20).order('id asc').unscope(:order)
SELECT * FROM articles WHERE id > 10 LIMIT 20
- ํน์ WHERE์ ์ ๋ฒ์๋ฅผ ํด์ ํ ์ ์์ต๋๋ค.
Article.where(id: 10, trashed: false).unscope(where: :id)
SELECT "articles".* FROM "articles" WHERE trashed = 0
- unscope๋ฅผ ์ฌ์ฉํ ๊ด๊ณ(relation)๋ merge ๊ด๊ณ์ ์์ด๋ ์ํฅ์ ๋ฏธ์น๊ฒ ๋ฉ๋๋ค.
Article.order('id asc').merge(Article.unscope(:order))
SELECT "articles".* FROM "articles"
only
- ์กฐ๊ฑด์ ์ฌ์ ์ํ ์ ๋ ์์ต๋๋ค.
Article.where('id > 10').limit(20).order('id desc').only(:order, :where)
SELECT * FROM articles WHERE id > 10 ORDER BY id DESC
#only ์๋ query
SELECT * FROM articles WHERE id > 10 ORDER BY id DESC LIMIT 20
reselect
Post.select(:title, :body).reselect(:created_at)
SELECT 'posts'.'created_at' FROM 'posts'
# reselect๊ฐ ์๋ ์ฟผ๋ฆฌ
SELECT `posts`.`title`, `posts`.`body`, `posts`.`created_at` FROM `posts`
reorder
- ์์๋ฅผ ์ฌ์ ์ ํฉ๋๋ค.
class Article < ApplicationRecord
has_many :comments, -> { order('posted_at DESC') }
end
Article.find(10).comments.reorder('name')
SELECT * FROM articles WHERE id = 10 LIMIT 1
SELECT * FROM comments WHERE article_id = 10 ORDER BY name
# reorder๋ฅผ ์ฌ์ฉํ์ง ์์ ๊ฒฝ์ฐ
SELECT * FROM articles WHERE id = 10 LIMIT 1
SELECT * FROM comments WHERE article_id = 10 ORDER BY posted_at DESC
reverse_order
- ์ง์ ๋ ์์๋ฅผ ๋ฐ๋๋ก ์ค์ ํฉ๋๋ค.
Client.where("orders_count > 10").order(:name).reverse_order
SELECT * FROM clients WHERE orders_count > 10 ORDER BY name DESC
- order๊ฐ ์ค์ ๋์ง ์์ผ๋ฉด ๊ธฐ๋ณธํค ๊ธฐ์ค์ผ๋ก reverse ๋ฉ๋๋ค.
rewhere
- where ์กฐ๊ฑด์ ์ฌ์ ์ ํฉ๋๋ค.
Article.where(trashed: true).rewhere(trashed: false)
SELECT * FROM articles WHERE `trashed` = 0
- rewhere์ ์ฌ์ฉํ์ง ์์ผ๋ฉด ์๋์ ๊ฐ์ด ๋์ค๊ฒ ๋ฉ๋๋ค.
Article.where(trashed: true).where(trashed: false)
SELECT * FROM articles WHERE `trashed` = 1 AND 'trashed' = 0
๐ญ Null
- none ๋ฉ์๋๋ ๋ ์ฝ๋๊ฐ ์๋ ์ฐ๊ฒฐ ๊ฐ๋ฅํ ๊ด๊ณ๋ฅดใ ใน ๋ฐํํฉ๋๋ค.
- ๋ฐํ๋ ๊ด๊ณ์ ์ฐ๊ฒฐ๋ ๋ชจ๋ ํ์ ์กฐ๊ฑด์ ๊ณ์ ๋น ๊ด๊ณ๋ฅผ ์์ฑํฉ๋๋ค.
Article.none
๐ฎ readOnly
- ์ฝ๊ธฐ ์ ์ฉ Model๋ก ์์ ์ด ํ์ฉ๋์ง ์๋ ๋ถ๋ณ์ ๋๋ค.
- ๋ณ๊ฒฝํ๋ฉด ActiveRecord::ReadOnlyRecord ์์ธ๊ฐ ๋ฐ์ํฉ๋๋ค.
Client.readonly.first
client.visits += 1
client.save # ์์ธ ๋ฐ์
๐ ๊ฒฐ๋ก
- Rails๋ Active Record๋ก ORM ๊ธฐ๋ฅ์ ์ง์ํฉ๋๋ค.
- Query Interface๋ก ๊ฐ๋ฐ์๊ฐ Query๋ฅผ ์์ฑํ์ง ์๊ณ ๋ DB SQL์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
- ๋๋ถ๋ถ์ Query ๋ช ๋ น๋ฌธ์ ์ง์ํ๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฐ์๊ฐ Query์ ์ง์คํ ํ์๊ฐ ์์ผ๋ฉฐ vender์ ๋ง์ถฐ SQL์ด ์กฐ๊ธ์ฉ ๋ค๋ฅธ๋ฐ ์ ์ฝ๊ฒ vender๋ฅผ ๋ณ๊ฒฝํ ์ ์๋ค๋ ์ฅ์ ๋ ๊ฐ์ง๊ณ ์์ต๋๋ค.
- 2ํธ์์๋ Lock, Join, Eger Loading ๋ฑ์ ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
์ถ์ฒ
728x90
728x90
'Backend > RubyOnRails' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
How Rails Sessions Work(์ด๋ป๊ฒ ๋ ์ผ์ฆ ์ธ์ ์ ๋์ํ๋๊ฐ) (0) | 2021.12.24 |
---|---|
[RubyOnRails Guides] Active Record Query Interface - 2ํธ (0) | 2021.03.09 |
[RubyOnRails Guides] Active Record Basics (0) | 2021.02.17 |
RubyOnRails Getting Start(Blog ๋ง๋ค๊ธฐ) 4ํธ(๋ง์ง๋ง๐จโ๐จ) (0) | 2021.02.16 |
RubyOnRails Getting Start(Blog ๋ง๋ค๊ธฐ) 3ํธ (0) | 2021.02.16 |