orderBy๋ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ํน์ ์ปฌ๋ผ ๊ธฐ์ค์ผ๋ก ์ ๋ ฌํ๋ ๋ฉ์๋์
๋๋ค. ์ค๋ฆ์ฐจ์(ASC) ๋๋ ๋ด๋ฆผ์ฐจ์(DESC)์ผ๋ก ์ ๋ ฌํ ์ ์์ผ๋ฉฐ, ์ฌ๋ฌ ์ปฌ๋ผ์ผ๋ก ์ ๋ ฌํ ์ ์์ต๋๋ค.
๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
์ค๋ฆ์ฐจ์ (ASC)
const users = await puri
.table("users")
.select({ id: "users.id", name: "users.name" })
.orderBy("users.name", "asc");
// ORDER BY users.name ASC
๋ด๋ฆผ์ฐจ์ (DESC)
const posts = await puri
.table("posts")
.select({ id: "posts.id", title: "posts.title", created_at: "posts.created_at" })
.orderBy("posts.created_at", "desc");
// ORDER BY posts.created_at DESC
์ฌ๋ฌ ์ปฌ๋ผ ์ ๋ ฌ
orderBy๋ฅผ ์ฒด์ด๋ํ์ฌ ์ฌ๋ฌ ์ปฌ๋ผ์ผ๋ก ์ ๋ ฌํ ์ ์์ต๋๋ค.
const users = await puri
.table("users")
.select({ id: "users.id", role: "users.role", name: "users.name" })
.orderBy("users.role", "asc") // ์ฒซ ๋ฒ์งธ: role ์ค๋ฆ์ฐจ์
.orderBy("users.name", "asc"); // ๋ ๋ฒ์งธ: name ์ค๋ฆ์ฐจ์
// ORDER BY users.role ASC, users.name ASC
์ ๋ ฌ ์ฐ์ ์์:
- ์ฒซ ๋ฒ์งธ
orderBy - ์ฃผ ์ ๋ ฌ
- ๋ ๋ฒ์งธ
orderBy - ๋ถ ์ ๋ ฌ (์ฃผ ์ ๋ ฌ์ด ๊ฐ์ ๋)
- ์ธ ๋ฒ์งธ
orderBy - ์ผ์ฐจ ์ ๋ ฌ (๋ถ ์ ๋ ฌ๋ ๊ฐ์ ๋)
์ค์ ์์
์ต์ ์
์ํ๋ฒณ์
๋ณตํฉ ์ ๋ ฌ
์ซ์ ์ ๋ ฌ
NULL ์ฒ๋ฆฌ
// ์ต์ ๊ฒ์๋ฌผ ๋จผ์
const posts = await puri.table("posts")
.select({
id: "posts.id",
title: "posts.title",
created_at: "posts.created_at"
})
.orderBy("posts.created_at", "desc")
.limit(20);
// ๊ฐ์ฅ ์ต๊ทผ์ ์
๋ฐ์ดํธ๋ ๊ฒ ๋จผ์
const users = await puri.table("users")
.select({
id: "users.id",
name: "users.name",
updated_at: "users.updated_at"
})
.orderBy("users.updated_at", "desc");
// ์ด๋ฆ ๊ฐ๋๋ค์
const users = await puri.table("users")
.select({
id: "users.id",
name: "users.name"
})
.orderBy("users.name", "asc");
// ์ ๋ชฉ ์ํ๋ฒณ ์ญ์
const products = await puri.table("products")
.select({
id: "products.id",
name: "products.name"
})
.orderBy("products.name", "desc");
// ์ฐ์ ์์: ์ํ โ ์์ฑ์ผ
const orders = await puri.table("orders")
.select({
id: "orders.id",
status: "orders.status",
created_at: "orders.created_at"
})
.orderBy("orders.status", "asc") // pending โ processing โ completed
.orderBy("orders.created_at", "desc"); // ์ต์ ์
// ์ฐ์ ์์: featured โ ํ์ โ ํ๋งค๋
const products = await puri.table("products")
.select({
id: "products.id",
name: "products.name",
featured: "products.featured",
rating: "products.rating",
sales: "products.sales"
})
.orderBy("products.featured", "desc") // ์ถ์ฒ ์ํ ๋จผ์
.orderBy("products.rating", "desc") // ๋์ ํ์
.orderBy("products.sales", "desc"); // ๋ง์ด ํ๋ฆฐ ๊ฒ
// ๊ฐ๊ฒฉ ๋ฎ์ ์
const products = await puri.table("products")
.select({
id: "products.id",
name: "products.name",
price: "products.price"
})
.where("products.published", true)
.orderBy("products.price", "asc");
// ์กฐํ์ ๋์ ์
const posts = await puri.table("posts")
.select({
id: "posts.id",
title: "posts.title",
views: "posts.views"
})
.orderBy("posts.views", "desc")
.limit(10);
// NULL์ ๋ง์ง๋ง์ผ๋ก
const users = await puri.table("users")
.select({
id: "users.id",
name: "users.name",
last_login: "users.last_login"
})
.orderBy("users.last_login", "desc"); // NULL์ ์๋์ผ๋ก ๋ง์ง๋ง
// NULL์ ๋จผ์
const posts = await puri.table("posts")
.select({
id: "posts.id",
title: "posts.title",
deleted_at: "posts.deleted_at"
})
.orderBy("posts.deleted_at", "asc"); // NULL์ด ๋จผ์ ์ด
์กฐ์ธ๋ ํ
์ด๋ธ ์ ๋ ฌ
์กฐ์ธํ ํ
์ด๋ธ์ ์ปฌ๋ผ์ผ๋ก๋ ์ ๋ ฌํ ์ ์์ต๋๋ค.
const posts = await puri
.table("posts")
.join("users", "posts.user_id", "users.id")
.select({
post_id: "posts.id",
title: "posts.title",
author_name: "users.name",
})
.orderBy("users.name", "asc") // ์์ฑ์ ์ด๋ฆ์
.orderBy("posts.created_at", "desc"); // ๊ฐ์ ์์ฑ์๋ ์ต์ ์
// ORDER BY users.name ASC, posts.created_at DESC
SELECT ๋ณ์นญ์ผ๋ก ์ ๋ ฌ
SELECT์์ ์ง์ ํ ๋ณ์นญ์ผ๋ก๋ ์ ๋ ฌํ ์ ์์ต๋๋ค.
const posts = await puri
.table("posts")
.select({
id: "posts.id",
title: "posts.title",
view_count: "posts.views", // ๋ณ์นญ ์ง์
})
.orderBy("view_count", "desc"); // ๋ณ์นญ ์ฌ์ฉ
// SELECT posts.views as view_count
// ORDER BY view_count DESC
์ง๊ณ ํจ์ ์ ๋ ฌ
GROUP BY์ ํจ๊ป ์ง๊ณ ํจ์ ๊ฒฐ๊ณผ๋ก ์ ๋ ฌํ ์ ์์ต๋๋ค.
const userStats = await puri
.table("posts")
.select({
user_id: "posts.user_id",
post_count: Puri.count(),
})
.groupBy("posts.user_id")
.orderBy("post_count", "desc") // ๊ฒ์๋ฌผ ๋ง์ ์
.limit(10);
// SELECT user_id, COUNT(*) as post_count
// FROM posts
// GROUP BY user_id
// ORDER BY post_count DESC
// LIMIT 10
SQL ํํ์์ผ๋ก ์ ๋ ฌ
orderBy๋ ์ปฌ๋ผ๋ช
๋์ SqlExpression<"number"> ๋๋ SqlExpression<"string">์ ๋ฐ์ ์ ์์ต๋๋ค. ์ ๋ฌธ ๊ฒ์ ์์, ์ ์ฌ๋ ์ ์ ๋ฑ ๊ณ์ฐ๋ ๊ฐ์ ๊ธฐ์ค์ผ๋ก ์ ๋ ฌํ ๋ ์ฌ์ฉํฉ๋๋ค.
๊ฒ์ ์์ ๊ธฐ์ค ์ ๋ ฌ
// ts_rank๋ก ์ ๋ฌธ ๊ฒ์ ๊ฒฐ๊ณผ๋ฅผ ๊ด๋ จ๋ ์์ผ๋ก ์ ๋ ฌ
const posts = await puri
.table("posts")
.select({
id: "posts.id",
title: "posts.title",
rank: Puri.tsRank("posts.content_tsv", "typescript"),
})
.orderBy(Puri.tsRank("posts.content_tsv", "typescript"), "desc");
// ORDER BY ts_rank(posts.content_tsv, plainto_tsquery('simple', ?)) DESC
์ ์ฌ๋ ์ ์ ๊ธฐ์ค ์ ๋ ฌ
// pg_trgm similarity๋ก ์ ์ฌ๋ ๋์ ์ ์ ๋ ฌ
const users = await puri
.table("users")
.select({
id: "users.id",
name: "users.name",
score: Puri.similarity("users.name", "ํ๊ธธ๋"),
})
.orderBy(Puri.similarity("users.name", "ํ๊ธธ๋"), "desc");
// ORDER BY similarity(users.name, ?) DESC
rawNumber๋ก ์ปค์คํ
์ ๋ ฌ
// ์ปค์คํ
SQL ํํ์์ผ๋ก ์ ๋ ฌ
const products = await puri
.table("products")
.select({
id: "products.id",
name: "products.name",
weighted: Puri.rawNumber("(products.views * 0.3 + products.likes * 0.7)"),
})
.orderBy(Puri.rawNumber("(products.views * 0.3 + products.likes * 0.7)"), "desc");
// ORDER BY (products.views * 0.3 + products.likes * 0.7) DESC
SQL ํํ์์ ์ฌ์ฉํ ๋๋ ๋ด๋ถ์ ์ผ๋ก orderByRaw๊ฐ ์คํ๋๋ฉฐ, ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ์ด ์๋์ผ๋ก
์ ์ฉ๋ฉ๋๋ค.
์ ๋ ฌ ๋ฐฉํฅ
ASC (์ค๋ฆ์ฐจ์)
- ์ซ์: ์์ ๊ฐ โ ํฐ ๊ฐ
- ๋ฌธ์: A โ Z, ๊ฐ โ ํ
- ๋ ์ง: ๊ณผ๊ฑฐ โ ๋ฏธ๋
- NULL: ๋ง์ง๋ง
.orderBy("users.age", "asc")
// 18, 25, 30, 35, 40, NULL
DESC (๋ด๋ฆผ์ฐจ์)
- ์ซ์: ํฐ ๊ฐ โ ์์ ๊ฐ
- ๋ฌธ์: Z โ A, ํ โ ๊ฐ
- ๋ ์ง: ๋ฏธ๋ โ ๊ณผ๊ฑฐ
- NULL: ๋ง์ง๋ง
.orderBy("users.age", "desc")
// 40, 35, 30, 25, 18, NULL
๊ธฐ๋ณธ๊ฐ์ ASC์
๋๋ค. ๋ฐฉํฅ์ ์ง์ ํ์ง ์์ผ๋ฉด ์ค๋ฆ์ฐจ์์ผ๋ก ์ ๋ ฌ๋ฉ๋๋ค.
์ฑ๋ฅ ์ต์ ํ
1. ์ธ๋ฑ์ค ํ์ฉ
// โ
์ข์: ์ธ๋ฑ์ค ์ปฌ๋ผ์ผ๋ก ์ ๋ ฌ
.orderBy("users.created_at", "desc") // created_at์ ์ธ๋ฑ์ค ํ์
// โ ๋์จ: ์ธ๋ฑ์ค ์๋ ์ปฌ๋ผ์ผ๋ก ์ ๋ ฌ (๋๋ฆผ)
.orderBy("users.bio", "asc")
๊ถ์ฅ ์ธ๋ฑ์ค:
CREATE INDEX idx_users_created_at ON users(created_at DESC);
CREATE INDEX idx_posts_published_created ON posts(published, created_at DESC);
2. LIMIT๊ณผ ํจ๊ป ์ฌ์ฉ
// โ
์ข์: ์์ N๊ฐ๋ง ์ ๋ ฌ
.orderBy("posts.views", "desc")
.limit(10) // ์ ์ฒด๋ฅผ ์ ๋ ฌํ์ง ์๊ณ ์์ 10๊ฐ๋ง
// โ ๏ธ ์ฃผ์: LIMIT ์์ผ๋ฉด ๋ชจ๋ ํ ์ ๋ ฌ (๋๋ฆผ)
.orderBy("posts.views", "desc")
3. ๋ณตํฉ ์ธ๋ฑ์ค
์ฌ๋ฌ ์ปฌ๋ผ์ผ๋ก ์ ๋ ฌํ ๋๋ ๋ณตํฉ ์ธ๋ฑ์ค๊ฐ ํ์ํฉ๋๋ค.
// ์ด ์ฟผ๋ฆฌ์๋
.orderBy("products.category", "asc")
.orderBy("products.price", "asc")
// ์ด๋ฐ ์ธ๋ฑ์ค๊ฐ ํ์
CREATE INDEX idx_products_category_price ON products(category, price);
์ฃผ์์ฌํญ
1. ์ ๋ ฌ ์์
// orderBy ์์๊ฐ ์ค์ํจ
// โ
์ฌ๋ฐ๋ฆ: ์ํ โ ๋ ์ง
.orderBy("orders.status", "asc")
.orderBy("orders.created_at", "desc")
// โ ์๋์ ๋ค๋ฆ: ๋ ์ง โ ์ํ
.orderBy("orders.created_at", "desc")
.orderBy("orders.status", "asc")
2. NULL ๊ฐ
// PostgreSQL๊ณผ MySQL์ NULL ์ ๋ ฌ์ด ๋ค๋ฅผ ์ ์์
// PostgreSQL: NULL LAST (๊ธฐ๋ณธ)
.orderBy("users.last_login", "desc")
// ๋ช
์์ ์ผ๋ก ์ง์ ํ๋ ค๋ฉด raw SQL
.whereRaw("ORDER BY users.last_login DESC NULLS FIRST")
3. ๋์๋ฌธ์ ๊ตฌ๋ถ
// โ ๏ธ ์ฃผ์: ๋์๋ฌธ์ ๊ตฌ๋ถ ์ ๋ ฌ
.orderBy("users.name", "asc")
// "Alice", "Bob", "alice", "bob"
// ๋์๋ฌธ์ ๋ฌด์ํ๋ ค๋ฉด
.orderBy(puri.raw("LOWER(users.name)"), "asc")
// "Alice", "alice", "Bob", "bob"
4. ์ฑ๋ฅ ์ํฅ
// โ ๋์จ: ๋์ฉ๋ ๋ฐ์ดํฐ ์ ์ฒด ์ ๋ ฌ
const allUsers = await puri.table("users").orderBy("users.created_at", "desc"); // ์๋ฐฑ๋ง ํ ์ ๋ ฌ
// โ
์ข์: LIMIT์ผ๋ก ์ ํ
const recentUsers = await puri.table("users").orderBy("users.created_at", "desc").limit(100); // ์์ 100๊ฐ๋ง
ํ์
์์ ์ฑ
orderBy๋ ํ์
์์ ํ๊ฒ ์ปฌ๋ผ์ ๊ฒ์ฆํฉ๋๋ค.
// โ
์ฌ๋ฐ๋ฅธ ์ปฌ๋ผ
await puri.table("users").orderBy("users.created_at", "desc");
// โ ํ์
์๋ฌ: ์กด์ฌํ์ง ์๋ ์ปฌ๋ผ
await puri.table("users").orderBy("users.unknown_field", "desc");
// โ ํ์
์๋ฌ: ์๋ชป๋ ๋ฐฉํฅ
await puri.table("users").orderBy("users.name", "invalid"); // "asc" ๋๋ "desc"๋ง ๊ฐ๋ฅ
๋ค์ ๋จ๊ณ
limit
๊ฒฐ๊ณผ ๊ฐ์ ์ ํํ๊ธฐ
where
์กฐ๊ฑด ํํฐ๋งํ๊ธฐ
select
์กฐํํ ํ๋ ์ ํํ๊ธฐ
join
ํ
์ด๋ธ ์กฐ์ธํ๊ธฐ