๋ฉ”์ธ ์ฝ˜ํ…์ธ ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ
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
์ •๋ ฌ ์šฐ์„ ์ˆœ์œ„:
  1. ์ฒซ ๋ฒˆ์งธ orderBy - ์ฃผ ์ •๋ ฌ
  2. ๋‘ ๋ฒˆ์งธ orderBy - ๋ถ€ ์ •๋ ฌ (์ฃผ ์ •๋ ฌ์ด ๊ฐ™์„ ๋•Œ)
  3. ์„ธ ๋ฒˆ์งธ orderBy - ์‚ผ์ฐจ ์ •๋ ฌ (๋ถ€ ์ •๋ ฌ๋„ ๊ฐ™์„ ๋•Œ)

์‹ค์ „ ์˜ˆ์‹œ

// ์ตœ์‹  ๊ฒŒ์‹œ๋ฌผ ๋จผ์ €
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 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

์ •๋ ฌ ๋ฐฉํ–ฅ

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"๋งŒ ๊ฐ€๋Šฅ

๋‹ค์Œ ๋‹จ๊ณ„