๋งˆ๋ฒ•์˜ sql ์—ฐ์‚ฐ์ž ๐Ÿช„

ORM ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์ž‘์—…ํ•  ๋•Œ, ์ œ๊ณต๋˜๋Š” ORM ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ ํŠน์ • ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์–ด๋ ค์šด ๊ฒฝ์šฐ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ƒํ™ฉ์—์„œ๋Š” ์›์‹œ ๋ฌธ์ž์—ด๋กœ ์ฟผ๋ฆฌ๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” raw ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ raw ์ฟผ๋ฆฌ๋Š” ํƒ€์ž… ์•ˆ์ „์„ฑ๊ณผ ์ฟผ๋ฆฌ ๋งค๊ฐœ๋ณ€์ˆ˜ํ™”์˜ ์ด์ ์ด ๋ถ€์กฑํ•œ ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค.

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋งŽ์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ sql ํ…œํ”Œ๋ฆฟ ๊ฐœ๋…์„ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋”์šฑ ํƒ€์ž… ์•ˆ์ „ํ•˜๊ณ  ๋งค๊ฐœ๋ณ€์ˆ˜ํ™”๋œ ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์–ด ์ฝ”๋“œ์˜ ์ „๋ฐ˜์ ์ธ ์•ˆ์ „์„ฑ๊ณผ ์œ ์—ฐ์„ฑ์ด ํ–ฅ์ƒ๋ฉ๋‹ˆ๋‹ค. ๊ฐ•๋ ฅํ•œ ORM ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ Drizzle๋„ sql ํ…œํ”Œ๋ฆฟ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

Drizzle์˜ sql ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ฟผ๋ฆฌ ์ž‘์„ฑ์— ์žˆ์–ด ๋” ๋‚˜์•„๊ฐˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์ฟผ๋ฆฌ ๋นŒ๋”๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ „์ฒด ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐ ์–ด๋ ค์›€์ด ์žˆ๋Š” ๊ฒฝ์šฐ, Drizzle ์ฟผ๋ฆฌ์˜ ํŠน์ • ์„น์…˜ ๋‚ด์—์„œ sql ํ…œํ”Œ๋ฆฟ์„ ์„ ํƒ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์œ ์—ฐ์„ฑ์„ ํ†ตํ•ด ๋ถ€๋ถ„ SELECT ๋ฌธ, WHERE ์ ˆ, ORDER BY ์ ˆ, HAVING ์ ˆ, GROUP BY ์ ˆ, ๊ทธ๋ฆฌ๊ณ  ๊ด€๊ณ„ํ˜• ์ฟผ๋ฆฌ ๋นŒ๋”์—์„œ๋„ sql ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Drizzle์—์„œ sql ํ…œํ”Œ๋ฆฟ์˜ ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•˜๋ฉด ์›ํ•˜๋Š” ์ฟผ๋ฆฌ ๊ตฌ์กฐ์™€ ๋ณต์žก์„ฑ์„ ๋‹ฌ์„ฑํ•˜๋ฉด์„œ๋„ ํƒ€์ž… ์•ˆ์ „์„ฑ๊ณผ ์ฟผ๋ฆฌ ๋งค๊ฐœ๋ณ€์ˆ˜ํ™”์˜ ์žฅ์ ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด์—์„œ ๋”์šฑ ๊ฒฌ๊ณ ํ•˜๊ณ  ์œ ์ง€ ๊ด€๋ฆฌ๊ฐ€ ์šฉ์ดํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

sqlโ€œ ํ…œํ”Œ๋ฆฟ

๋‹ค๋ฅธ ORM์—์„œ๋„ ํ”ํžˆ ์ ‘ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ์‚ฌ์šฉ๋ฒ• ์ค‘ ํ•˜๋‚˜๋Š” raw ์ฟผ๋ฆฌ๋ฅผ ์œ„ํ•ด sql ์ฟผ๋ฆฌ๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

import { sql } from 'drizzle-orm'

const id = 69;
await db.execute(sql`select * from ${usersTable} where ${usersTable.id} = ${id}`)

๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฟผ๋ฆฌ๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค

select * from "users" where "users"."id" = $1; --> [69]

sql ๋งค๊ฐœ๋ณ€์ˆ˜์— ์ œ๊ณต๋œ ๋ชจ๋“  ํ…Œ์ด๋ธ”๊ณผ ์ปฌ๋Ÿผ์€ ์ž๋™์œผ๋กœ ํ•ด๋‹น SQL ๊ตฌ๋ฌธ์— ๋งคํ•‘๋˜๋ฉฐ, ํ…Œ์ด๋ธ”์— ๋Œ€ํ•ด์„œ๋Š” ์ด์Šค์ผ€์ดํ”„๋œ ์ด๋ฆ„์ด ์‚ฌ์šฉ๋˜๊ณ , ์ด์Šค์ผ€์ดํ”„๋œ ํ…Œ์ด๋ธ” ์ด๋ฆ„์ด ์ปฌ๋Ÿผ ์ด๋ฆ„์— ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ ${id}์™€ ๊ฐ™์€ ๋™์  ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” $1 ํ”Œ๋ ˆ์ด์Šคํ™€๋”๋กœ ๋งคํ•‘๋˜๋ฉฐ, ํ•ด๋‹น ๊ฐ’์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ณ„๋„๋กœ ์ „๋‹ฌ๋˜๋Š” ๊ฐ’ ๋ฐฐ์—ด๋กœ ์ด๋™๋ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ์ ‘๊ทผ ๋ฐฉ์‹์€ ์ž ์žฌ์ ์ธ SQL ์ธ์ ์…˜ ์ทจ์•ฝ์ ์„ ํšจ๊ณผ์ ์œผ๋กœ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.

sql<T>

sql<T>๋Š” ๋Ÿฐํƒ€์ž„ ๋งคํ•‘์„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. sql<T>๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ •์˜ํ•˜๋Š” ํƒ€์ž…์€ ์ˆœ์ „ํžˆ Drizzle์„ ์œ„ํ•œ ํ—ฌํผ์ž…๋‹ˆ๋‹ค. SQL ์ฟผ๋ฆฌ๋Š” ๋งค์šฐ ๋‹ค์–‘ํ•˜๊ณ  ์‚ฌ์šฉ์ž ์ •์˜๊ฐ€ ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ •ํ™•ํ•œ ํƒ€์ž…์„ ๋™์ ์œผ๋กœ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋Š” ์‹คํ˜„ ๊ฐ€๋Šฅํ•œ ๋ฐฉ๋ฒ•์ด ์—†๋‹ค๋Š” ์ ์„ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

unknown ์ด์™ธ์˜ ํŠน์ • ํƒ€์ž…์ด ํ•„์š”ํ•œ ํ•„๋“œ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก Drizzle์—์„œ ์ปค์Šคํ…€ ํƒ€์ž…์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๊ธฐ๋Šฅ์€ ๋ถ€๋ถ„ select ์ฟผ๋ฆฌ์—์„œ ํŠนํžˆ ์œ ์šฉํ•˜๋ฉฐ, ์„ ํƒ๋œ ํ•„๋“œ์— ๋Œ€ํ•œ ์ผ๊ด€๋œ ํƒ€์ดํ•‘์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค:

// sql<T> ํƒ€์ž…์ด ์ •์˜๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ
const response: { lowerName: unknown }[] = await db.select({
    lowerName: sql`lower(${usersTable.id})`
}).from(usersTable);

// sql<T> ํƒ€์ž…์ด ์ •์˜๋œ ๊ฒฝ์šฐ
const response: { lowerName: string }[] = await db.select({
    lowerName: sql<string>`lower(${usersTable.id})`
}).from(usersTable);

sql``.mapWith()

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋“œ๋ผ์ด๋ฒ„์—์„œ drizzle๋กœ ์ „๋‹ฌ๋˜๋Š” ๊ฐ’์— ๋Œ€ํ•œ ๋Ÿฐํƒ€์ž„ ๋งคํ•‘์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ .mapWith()๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ํ•จ์ˆ˜๋Š” ๋Ÿฐํƒ€์ž„์— ์‘๋‹ต์„ ๋งคํ•‘ํ•  ๋‹ค์–‘ํ•œ ๊ฐ’์„ ๋ฐ›์Šต๋‹ˆ๋‹ค.

mapWith ๋‚ด๋ถ€์˜ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ Column์— ์˜ํ•ด ๊ตฌํ˜„๋œ ์ธํ„ฐํŽ˜์ด์Šค์™€ ๋™์ผํ•œ ํ•œ, ํŠน์ • ์ปฌ๋Ÿผ ๋งคํ•‘ ์ „๋žต์„ ๋ณต์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const usersTable = pgTable('users', {
    id: serial('id').primaryKey(),
    name: text('name').notNull(),
});

// ๋Ÿฐํƒ€์ž„์— ์ด ๊ฐ’๋“ค์€ drizzle์—์„œ `text` ์ปฌ๋Ÿผ์ด ๋งคํ•‘๋˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•˜๊ฒŒ ๋งคํ•‘๋ฉ๋‹ˆ๋‹ค
sql`...`.mapWith(usersTable.name);

DriverValueDecoder ์ธํ„ฐํŽ˜์ด์Šค์— ๋Œ€ํ•œ ์ž์ฒด ๊ตฌํ˜„์„ ์ „๋‹ฌํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค:

sql``.mapWith({
	mapFromDriverValue: (value: any) => {
		const mappedValue = value;
		// ์ ์šฉํ•˜๋ ค๋Š” ๋งคํ•‘
		return mappedValue;
	},
});

// ๋˜๋Š”
sql``.mapWith(Number);

sql``.as<T>()

๋‹ค์–‘ํ•œ ๊ฒฝ์šฐ์— ์‚ฌ์šฉํ•˜๋ ค๋Š” ์ปค์Šคํ…€ ํ•„๋“œ์˜ ์ด๋ฆ„์„ ์ง€์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๊ฒฐ์ •ํ•˜๊ธฐ๊ฐ€ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์„ ํƒ๋  ํ•„๋“œ์— ๋Œ€ํ•ด ๋ณ„์นญ์„ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •ํ•ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋ณต์žกํ•œ ์ฟผ๋ฆฌ๋ฅผ ๋‹ค๋ฃฐ ๋•Œ ํŠนํžˆ ์œ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณ„์นญ์„ ๋ช…์‹œ์ ์œผ๋กœ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋Š” ์œ ์šฉํ•œ .as('alias_name') ํ—ฌํผ๋ฅผ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•˜๋ฉด ํ•„๋“œ์— ๋Œ€ํ•ด ๋ช…ํ™•ํ•˜๊ณ  ์˜๋ฏธ ์žˆ๋Š” ์ด๋ฆ„์„ ์ œ๊ณตํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ๋”์šฑ ์ง๊ด€์ ์ด๊ณ  ์ฝ๊ธฐ ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

sql`lower(usersTable.name)`.as('lower_name')
... "usersTable"."name" as lower_name ...

sql.raw()

์ž…๋ ฅ์—์„œ ๋งค๊ฐœ๋ณ€์ˆ˜ํ™”๋œ ๊ฐ’์„ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ํ…Œ์ด๋ธ”/์ปฌ๋Ÿผ์„ ์ด์Šค์ผ€์ดํ”„๋œ ๊ฒƒ์œผ๋กœ ๋งคํ•‘ํ•  ํ•„์š”๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  ์ฟผ๋ฆฌ๋ฅผ ๊ทธ๋Œ€๋กœ ์ƒ์„ฑํ•˜๊ณ  ์‹ถ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ƒํ™ฉ์„ ์œ„ํ•ด sql.raw() ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

sql.raw() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ถ”๊ฐ€์ ์ธ ์ฒ˜๋ฆฌ๋‚˜ ์ด์Šค์ผ€์ดํ”„ ์—†์ด ์ฟผ๋ฆฌ ๋‚ด์— raw SQL ๋ฌธ์„ ํฌํ•จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์‚ฌ์ „์— ๊ตฌ์„ฑ๋œ SQL ๋ฌธ์ด ์žˆ๊ฑฐ๋‚˜ ๋ณต์žกํ•˜๊ฑฐ๋‚˜ ๋™์ ์ธ SQL ์ฝ”๋“œ๋ฅผ ์ฟผ๋ฆฌ์— ์ง์ ‘ ํ†ตํ•ฉํ•ด์•ผ ํ•  ๋•Œ ์œ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

sql.raw(`select * from users where id = ${12}`);
// vs
sql`select * from users where id = ${12}`;
select * from users where id = 12;
--> vs
select * from users where id = $1; --> [12]

sql ํ•จ์ˆ˜ ๋‚ด์—์„œ sql.raw()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฉ”์ธ sql ํ…œํ”Œ๋ฆฟ ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์ด์Šค์ผ€์ดํ”„ํ•˜์ง€ ์•Š๊ณ  raw ๋ฌธ์ž์—ด์„ ํฌํ•จํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

sql ํ•จ์ˆ˜ ๋‚ด์—์„œ sql.raw()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด์Šค์ผ€์ดํ”„๋˜์ง€ ์•Š์€ raw ๋ฌธ์ž์—ด์„ ์ฟผ๋ฆฌ์— ์ง์ ‘ ํ†ตํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ํ…œํ”Œ๋ฆฟ ํ•จ์ˆ˜์˜ ์ž๋™ ์ด์Šค์ผ€์ดํ”„๋‚˜ ์ˆ˜์ •์˜ ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š์•„์•ผ ํ•˜๋Š” ํŠน์ • SQL ์ฝ”๋“œ๋‚˜ ํ‘œํ˜„์‹์ด ์žˆ์„ ๋•Œ ํŠนํžˆ ์œ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

sql`select * from ${usersTable} where id = ${12}`;
// vs
sql`select * from ${usersTable} where id = ${sql.raw(12)}`;
select * from "users" where id = $1; --> [12]
--> vs
select * from "users" where id = 12;

sql.fromList()

sql ํ…œํ”Œ๋ฆฟ์€ SQL ๋ถ€๋ถ„์˜ ๋ฐฐ์—ด์ธ sql ์ฒญํฌ๋ฅผ ์ƒ์„ฑํ•˜๋ฉฐ, ์ด๋Š” Drizzle์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋‚˜ ์ฟผ๋ฆฌ์— SQL์„ ์ ์šฉํ•œ ํ›„ ์ฟผ๋ฆฌ์™€ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค.

ํŠน์ • ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ๋Š” ์ปค์Šคํ…€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋Ÿฌํ•œ ์ฒญํฌ๋ฅผ ๋ฐฐ์—ด๋กœ ์ง‘๊ณ„ํ•œ ๋‹ค์Œ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋‚˜ ์ฟผ๋ฆฌ์— ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋Š” ๋‹จ์ผ SQL ๋ฌธ์œผ๋กœ ์—ฐ๊ฒฐํ•ด์•ผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ fromList ํ•จ์ˆ˜๊ฐ€ ๋งค์šฐ ์œ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

fromList ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—ฌ๋Ÿฌ SQL ์ฒญํฌ๋ฅผ ๋‹จ์ผ SQL ๋ฌธ์œผ๋กœ ๊ฒฐํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŠน์ • ์š”๊ตฌ ์‚ฌํ•ญ์— ๋”ฐ๋ผ ๊ฐœ๋ณ„ SQL ๋ถ€๋ถ„์„ ์ง‘๊ณ„ํ•˜๊ณ  ์—ฐ๊ฒฐํ•œ ๋‹ค์Œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ํ†ตํ•ฉ๋œ SQL ์ฟผ๋ฆฌ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const sqlChunks: SQL[] = [];

sqlChunks.push(sql`select * from users`);

// ์ผ๋ถ€ ๋กœ์ง

sqlChunks.push(sql` where `);

// ์ผ๋ถ€ ๋กœ์ง

for (let i = 0; i < 5; i++) {
	sqlChunks.push(sql`id = ${i}`);

	if (i === 4) continue;
	sqlChunks.push(sql` or `);
}

const finalSql: SQL = sql.fromList(sqlChunks)
select * from users where id = $1 or id = $2 or id = $3 or id = $4 or id = $5; --> [0, 1, 2, 3, 4]

sql.join()

์‹ค์ œ๋กœ sql.join ํ•จ์ˆ˜๋Š” fromList ํ—ฌํผ์™€ ์œ ์‚ฌํ•œ ๋ชฉ์ ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ SQL ์ฒญํฌ ์‚ฌ์ด์˜ ๊ณต๋ฐฑ์„ ์ฒ˜๋ฆฌํ•˜๊ฑฐ๋‚˜ SQL ์ฒญํฌ๋ฅผ ์—ฐ๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ์ปค์Šคํ…€ ๊ตฌ๋ถ„์ž๋ฅผ ์ง€์ •ํ•  ๋•Œ ์ถ”๊ฐ€์ ์ธ ์œ ์—ฐ์„ฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

sql.join์„ ์‚ฌ์šฉํ•˜๋ฉด ์ง€์ •๋œ ๊ตฌ๋ถ„์ž๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ SQL ์ฒญํฌ๋ฅผ ํ•จ๊ป˜ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ตฌ๋ถ„์ž๋Š” ์ฒญํฌ ์‚ฌ์ด์— ์‚ฝ์ž…ํ•˜๋ ค๋Š” ๋ชจ๋“  ๋ฌธ์ž์—ด์ด๋‚˜ ๋ฌธ์ž๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋Š” SQL ์ฒญํฌ์˜ ํ˜•์‹ ์ง€์ •์ด๋‚˜ ๊ตฌ๋ถ„์— ๋Œ€ํ•œ ํŠน์ • ์š”๊ตฌ ์‚ฌํ•ญ์ด ์žˆ์„ ๋•Œ ํŠนํžˆ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ปค์Šคํ…€ ๊ตฌ๋ถ„์ž๋ฅผ ์ง€์ •ํ•˜๋ฉด ์ตœ์ข… SQL ์ฟผ๋ฆฌ์—์„œ ์›ํ•˜๋Š” ๊ตฌ์กฐ์™€ ํ˜•์‹์„ ๋‹ฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const sqlChunks: SQL[] = [];

sqlChunks.push(sql`select * from users`);

// ์ผ๋ถ€ ๋กœ์ง

sqlChunks.push(sql`where`);

// ์ผ๋ถ€ ๋กœ์ง

for (let i = 0; i < 5; i++) {
	sqlChunks.push(sql`id = ${i}`);

if (i === 4) continue;
    sqlChunks.push(sql`or`);
}

const finalSql: SQL = sql.join(sqlChunks, sql.raw(' '));
select * from users where id = $1 or id = $2 or id = $3 or id = $4 or id = $5; --> [0, 1, 2, 3, 4]

sql.append()

sql ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฏธ SQL์„ ์ƒ์„ฑํ•œ ๊ฒฝ์šฐ, append ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒ์„ฑ๋œ SQL์— ์ง์ ‘ ์ƒˆ ์ฒญํฌ๋ฅผ ์ถ”๊ฐ€ํ•จ์œผ๋กœ์จ fromList์™€ ๋™์ผํ•œ ๋™์ž‘์„ ๋‹ฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

append ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ธฐ์กด SQL ๋ฌธ์ž์—ด์— ์ถ”๊ฐ€ SQL ์ฒญํฌ๋ฅผ ๋™์ ์œผ๋กœ ์ถ”๊ฐ€ํ•˜์—ฌ ํšจ๊ณผ์ ์œผ๋กœ ํ•จ๊ป˜ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ฒญํฌ๋ฅผ ์ตœ์ข… SQL ์ฟผ๋ฆฌ๋กœ ์ง‘๊ณ„ํ•˜๊ธฐ ์œ„ํ•œ ์ปค์Šคํ…€ ๋กœ์ง์ด๋‚˜ ๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™์„ ํ†ตํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const finalSql = sql`select * from users`;

// ์ผ๋ถ€ ๋กœ์ง

finalSql.append(sql` where `);

// ์ผ๋ถ€ ๋กœ์ง

for (let i = 0; i < 5; i++) {
	finalSql.append(sql`id = ${i}`);

	if (i === 4) continue;
	finalSql.append(sql` or `);
}
select * from users where id = $1 or id = $2 or id = $3 or id = $4 or id = $5; --> [0, 1, 2, 3, 4]

sql.empty()

sql.empty()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋นˆ SQL ๊ฐ์ฒด๋กœ ์‹œ์ž‘ํ•œ ๋‹ค์Œ ํ•„์š”์— ๋”ฐ๋ผ SQL ์ฒญํฌ๋ฅผ ๋™์ ์œผ๋กœ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ปค์Šคํ…€ ๋กœ์ง์ด๋‚˜ ์กฐ๊ฑด์„ ์ ์šฉํ•˜์—ฌ ๊ฐ ์ฒญํฌ์˜ ๋‚ด์šฉ์„ ๊ฒฐ์ •ํ•˜๋ฉด์„œ SQL ์ฟผ๋ฆฌ๋ฅผ ์ ์ง„์ ์œผ๋กœ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

sql.empty()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ SQL ๊ฐ์ฒด๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ ํ›„์—๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜ํ™”, ๊ตฌ์„ฑ, ์ด์Šค์ผ€์ดํ”„์™€ ๊ฐ™์€ sql ํ…œํ”Œ๋ฆฟ์˜ ์ „์ฒด ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ํŠน์ • ์š”๊ตฌ ์‚ฌํ•ญ์— ๋งž๊ฒŒ ์œ ์—ฐํ•˜๊ณ  ํ†ต์ œ๋œ ๋ฐฉ์‹์œผ๋กœ SQL ์ฟผ๋ฆฌ๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const finalSql = sql.empty();

// ์ผ๋ถ€ ๋กœ์ง

finalSql.append(sql`select * from users`);

// ์ผ๋ถ€ ๋กœ์ง

finalSql.append(sql` where `);

// ์ผ๋ถ€ ๋กœ์ง

for (let i = 0; i < 5; i++) {
	finalSql.append(sql`id = ${i}`);

	if (i === 4) continue;
	finalSql.append(sql` or `);
}
select * from users where id = $1 or id = $2 or id = $3 or id = $4 or id = $5; --> [0, 1, 2, 3, 4]

sql์„ ๋ฌธ์ž์—ด๊ณผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ

์•ž์˜ ๋ชจ๋“  ์˜ˆ์ œ์—์„œ TypeScript์˜ SQL ํ…œํ”Œ๋ฆฟ ๊ตฌ๋ฌธ ์‚ฌ์šฉ๊ณผ ์ƒ์„ฑ๋œ SQL ์ถœ๋ ฅ์„ ๊ด€์ฐฐํ–ˆ์Šต๋‹ˆ๋‹ค.

SQL ํ…œํ”Œ๋ฆฟ์—์„œ ์ƒ์„ฑ๋œ ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด๊ณผ ํ•ด๋‹น ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์–ป์œผ๋ ค๋ฉด, ์ฟผ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•˜๋ ค๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐฉ์–ธ์„ ์ง€์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋งˆ๋‹ค ๋งค๊ฐœ๋ณ€์ˆ˜ํ™” ๋ฐ ์ด์Šค์ผ€์ดํ”„์— ๋Œ€ํ•œ ๊ตฌ๋ฌธ์ด ๋‹ค๋ฅด๋ฏ€๋กœ ์ ์ ˆํ•œ ๋ฐฉ์–ธ์„ ์„ ํƒํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

๋ฐฉ์–ธ์„ ์„ ํƒํ•œ ํ›„์—๋Š” ํ•ด๋‹น ๊ตฌํ˜„์˜ ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•˜์—ฌ SQL ํ…œํ”Œ๋ฆฟ์„ ์›ํ•˜๋Š” ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด๊ณผ ๋งค๊ฐœ๋ณ€์ˆ˜ ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ž‘์—… ์ค‘์ธ ํŠน์ • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‹œ์Šคํ…œ๊ณผ์˜ ํ˜ธํ™˜์„ฑ์ด ๋ณด์žฅ๋ฉ๋‹ˆ๋‹ค.

PostgreSQL
MySQL
SQLite
import { PgDialect } from 'drizzle-orm/pg-core';

const pgDialect = new PgDialect();
pgDialect.sqlToQuery(sql`select * from ${usersTable} where ${usersTable.id} = ${12}`);
select * from "users" where "users"."id" = $1; --> [ 12 ]

select์—์„œ sql ์‚ฌ์šฉํ•˜๊ธฐ

๋ถ€๋ถ„ select ์ฟผ๋ฆฌ์—์„œ๋„ sql ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ถ€๋ถ„ select ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ „์ฒด ํ–‰์„ ๊ฐ€์ ธ์˜ค๋Š” ๋Œ€์‹  ํ…Œ์ด๋ธ”์—์„œ ํŠน์ • ํ•„๋“œ๋‚˜ ์ปฌ๋Ÿผ์„ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ถ€๋ถ„ select ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ๋” ์ž์„ธํ•œ ์ •๋ณด๋Š” **Core API ๋ฌธ์„œ**์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ…Œ์ด๋ธ”์—์„œ ๋‹ค์–‘ํ•œ ์ปค์Šคํ…€ ํ•„๋“œ ์„ ํƒํ•˜๊ธฐ

์—ฌ๊ธฐ์„œ sql<T>, sql``.mapWith(), **sql``.as<T>()**์˜ ์‚ฌ์šฉ๋ฒ•์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import { sql } from 'drizzle-orm'
import { usersTable } from 'schema'

await db.select({
    id: usersTable.id,
    lowerName: sql<string>`lower(${usersTable.name})`,
    aliasedName: sql<string>`lower(${usersTable.name})`.as('aliased_column'),
    count: sql<number>`count(*)`.mapWith(Number)
}).from(usersTable)
select `id`, lower(`name`), lower(`name`) as `aliased_column`, count(*) from `users`;

where์—์„œ sql ์‚ฌ์šฉํ•˜๊ธฐ

์‹ค์ œ๋กœ Drizzle์€ sql ํ…œํ”Œ๋ฆฟ ๋‚ด์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์—ฌ๋Ÿฌ ํ‘œํ˜„์‹์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ํ™•์žฅ ๊ธฐ๋Šฅ์ด๋‚˜ ๋‹ค๋ฅธ ์ˆ˜๋‹จ์„ ํ†ตํ•ด ์ œ๊ณต๋˜๋Š” ํ‘œํ˜„์‹์„ ํฌํ•จํ•˜์—ฌ ๋” ๊ด‘๋ฒ”์œ„ํ•œ ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์€ ๊ฒƒ์ด ์‚ฌ์‹ค์ž…๋‹ˆ๋‹ค.

์œ ์—ฐ์„ฑ์„ ๋ณด์žฅํ•˜๊ณ  Drizzle์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด, sql ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ SQL ํ…œํ”Œ๋ฆฟ์„ ์ง์ ‘ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด SQL์˜ ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•˜๊ณ  ๋Œ€์ƒ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ํŠนํ™”๋œ ํ‘œํ˜„์‹์ด๋‚˜ ๊ธฐ๋Šฅ์„ ํ†ตํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

sql ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•˜๋ฉด Drizzle์˜ ์‚ฌ์ „ ์ •์˜๋œ ํ‘œํ˜„์‹์—๋งŒ ์ œํ•œ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  ๋ณต์žกํ•œ ์ฟผ๋ฆฌ๋ฅผ ํ‘œํ˜„ํ•˜๊ณ  ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‹œ์Šคํ…œ์ด ์ œ๊ณตํ•˜๋Š” ์ง€์›๋˜๋Š” ํ‘œํ˜„์‹์„ ํ†ตํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

sql์„ ์‚ฌ์šฉํ•œ id๋กœ ํ•„ํ„ฐ๋งํ•˜๊ธฐ

import { sql } from 'drizzle-orm'
import { usersTable } from 'schema'

const id = 77

await db.select()
        .from(usersTable)
        .where(sql`${usersTable.id} = ${id}`)
select * from "users" where "users"."id" = $1; --> [ 77 ]

๊ณ ๊ธ‰ ์ „์ฒด ํ…์ŠคํŠธ ๊ฒ€์ƒ‰ where ๋ฌธ

import { sql } from 'drizzle-orm'
import { usersTable } from 'schema'

const searchParam = "Ale"

await db.select()
        .from(usersTable)
        .where(sql`to_tsvector('simple', ${usersTable.name}) @@ to_tsquery('simple', ${searchParam})`)
select * from "users" where to_tsvector('simple', "users"."name") @@ to_tsquery('simple', '$1'); --> [ "Ale" ]

orderBy์—์„œ sql ์‚ฌ์šฉํ•˜๊ธฐ

Drizzle์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ์ •๋ ฌ์„ ์œ„ํ•œ ํŠน์ • ๊ธฐ๋Šฅ์ด ํ•„์š”ํ•˜์ง€๋งŒ raw SQL์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์ง€ ์•Š์„ ๋•Œ ORDER BY ์ ˆ์—์„œ sql ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import { sql } from 'drizzle-orm'
import { usersTable } from 'schema'

await db.select().from(usersTable).orderBy(sql`${usersTable.id} desc nulls first`)
select * from "users" order by "users"."id" desc nulls first;

having๊ณผ groupBy์—์„œ sql ์‚ฌ์šฉํ•˜๊ธฐ

Drizzle์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ํŠน์ • ๊ธฐ๋Šฅ์ด ํ•„์š”ํ•˜์ง€๋งŒ raw SQL์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์ง€ ์•Š์„ ๋•Œ HAVING๊ณผ GROUP BY ์ ˆ์—์„œ sql ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import { sql } from 'drizzle-orm'
import { usersTable } from 'schema'

await db.select({
    projectId: usersTable.projectId,
    count: sql<number>`count(${usersTable.id})`.mapWith(Number)
}).from(usersTable)
    .groupBy(sql`${usersTable.projectId}`)
    .having(sql`count(${usersTable.id}) > 300`)
select "project_id", count("users"."id") from users group by "users"."project_id" having count("users"."id") > 300;