select, update, delete์ ํจ๊ป WITH ๋ฌธ์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ํ์ธํ์ธ์
SQL Insert
Drizzle ORM์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์ด๋ธ์ ํ์ ์ฝ์ ํ๋ ๊ฐ์ฅ SQL๊ณผ ์ ์ฌํ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค.
Insert one row
Drizzle๋ก ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ํ๋ ๊ฒ์ ๋งค์ฐ ๊ฐ๋จํ๊ณ SQL๊ณผ ์ ์ฌํฉ๋๋ค. ์ง์ ํ์ธํด๋ณด์ธ์:
await db.insert(users).values({ name: 'Andrew' });insert into "users" ("name") values ("Andrew");ํน์ ํ
์ด๋ธ์ insert ํ์
์ด ํ์ํ ๊ฒฝ์ฐ typeof usersTable.$inferInsert ๊ตฌ๋ฌธ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
type NewUser = typeof users.$inferInsert;
const insertUser = async (user: NewUser) => {
return db.insert(users).values(user);
}
const newUser: NewUser = { name: "Alef" };
await insertUser(newUser);Insert returning
PostgreSQL๊ณผ SQLite์์ ๋ค์๊ณผ ๊ฐ์ด ํ์ ์ฝ์ ํ๊ณ ๋ฐํ๋ฐ์ ์ ์์ต๋๋ค:
await db.insert(users).values({ name: "Dan" }).returning();
// partial return
await db.insert(users).values({ name: "Partial Dan" }).returning({ insertedId: users.id });Insert $returningId
MySQL ์์ฒด๋ INSERT ์ฌ์ฉ ํ RETURNING์ ๋ํ ๋ค์ดํฐ๋ธ ์ง์์ด ์์ต๋๋ค. autoincrement(๋๋ serial) ํ์
์ ๊ฐ์ง primary keys์ ๊ฒฝ์ฐ insertId์ affectedRows ํ๋์ ์ก์ธ์คํ ์ ์๋ ํ ๊ฐ์ง ๋ฐฉ๋ฒ๋ง ์์ต๋๋ค. Drizzle๋ก ์ด๋ฌํ ๊ฒฝ์ฐ๋ฅผ ์ฒ๋ฆฌํ๊ณ ์ฝ์
๋ ๋ชจ๋ ID๋ฅผ ๋ณ๋์ ๊ฐ์ฒด๋ก ์๋์ผ๋ก ๋ฐ์ ์ ์๋ ์๋ ๋ฐฉ๋ฒ์ ์ค๋นํ์ต๋๋ค
import { boolean, int, text, mysqlTable } from 'drizzle-orm/mysql-core';
const usersTable = mysqlTable('users', {
id: int('id').primaryKey(),
name: text('name').notNull(),
verified: boolean('verified').notNull().default(false),
});
const result = await db.insert(usersTable).values([{ name: 'John' }, { name: 'John1' }]).$returningId();
// ^? { id: number }[]๋ํ Drizzle์ ์ฌ์ฉํ๋ฉด ๋ฐํ์์ ์ฌ์ฉ์ ์ ์ ๊ธฐ๋ณธ ํค๋ฅผ ์์ฑํ๋ $default ํจ์๋ก primary key๋ฅผ ์ง์ ํ ์ ์์ต๋๋ค. $returningId() ํธ์ถ์์ ์์ฑ๋ ํค๋ ๋ฐํํด๋๋ฆฝ๋๋ค
import { varchar, text, mysqlTable } from 'drizzle-orm/mysql-core';
import { createId } from '@paralleldrive/cuid2';
const usersTableDefFn = mysqlTable('users_default_fn', {
customId: varchar('id', { length: 256 }).primaryKey().$defaultFn(createId),
name: text('name').notNull(),
});
const result = await db.insert(usersTableDefFn).values([{ name: 'John' }, { name: 'John1' }]).$returningId();
// ^? { customId: string }[]๊ธฐ๋ณธ ํค๊ฐ ์๋ ๊ฒฝ์ฐ -> ์ด๋ฌํ ์ฟผ๋ฆฌ์ ํ์ ์
{}[]์ด ๋ฉ๋๋ค
Insert multiple rows
await db.insert(users).values([{ name: 'Andrew' }, { name: 'Dan' }]);Upserts and conflicts
Drizzle ORM์ upsert์ ์ถฉ๋ ์ฒ๋ฆฌ๋ฅผ ์ํ ๊ฐ๋จํ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํฉ๋๋ค.
On conflict do nothing
onConflictDoNothing์ ์ถฉ๋์ด ์์ ๊ฒฝ์ฐ ์ฝ์
์ ์ทจ์ํฉ๋๋ค:
await db.insert(users)
.values({ id: 1, name: 'John' })
.onConflictDoNothing();
// explicitly specify conflict target
await db.insert(users)
.values({ id: 1, name: 'John' })
.onConflictDoNothing({ target: users.id });On conflict do update
onConflictDoUpdate๋ ์ถฉ๋์ด ์์ ๊ฒฝ์ฐ ํ์ ์
๋ฐ์ดํธํฉ๋๋ค:
await db.insert(users)
.values({ id: 1, name: 'Dan' })
.onConflictDoUpdate({ target: users.id, set: { name: 'John' } });where ์
on conflict do update๋ ๋ ๊ฐ์ง ๋ค๋ฅธ ์์น์ where ์ ์ ๊ฐ์ง ์ ์์ต๋๋ค -
์ถฉ๋ ๋์์ ์ผ๋ถ(์: ๋ถ๋ถ ์ธ๋ฑ์ค์ฉ) ๋๋ update ์ ์ ์ผ๋ถ๋ก:
insert into employees (employee_id, name)
values (123, 'John Doe')
on conflict (employee_id) where name <> 'John Doe'
do update set name = excluded.name
insert into employees (employee_id, name)
values (123, 'John Doe')
on conflict (employee_id) do update set name = excluded.name
where name <> 'John Doe';Drizzle์์ ์ด๋ฌํ ์กฐ๊ฑด์ ์ง์ ํ๋ ค๋ฉด setWhere์ targetWhere ์ ์ ์ฌ์ฉํ ์ ์์ต๋๋ค:
await db.insert(employees)
.values({ employeeId: 123, name: 'John Doe' })
.onConflictDoUpdate({
target: employees.employeeId,
targetWhere: sql`name <> 'John Doe'`,
set: { name: sql`excluded.name` }
});
await db.insert(employees)
.values({ employeeId: 123, name: 'John Doe' })
.onConflictDoUpdate({
target: employees.employeeId,
set: { name: 'John Doe' },
setWhere: sql`name <> 'John Doe'`
});๋ณตํฉ ์ธ๋ฑ์ค ๋๋ onConflictDoUpdate๋ฅผ ์ํ ๋ณตํฉ ๊ธฐ๋ณธ ํค๋ฅผ ์ฌ์ฉํ Upsert:
await db.insert(users)
.values({ firstName: 'John', lastName: 'Doe' })
.onConflictDoUpdate({
target: [users.firstName, users.lastName],
set: { firstName: 'John1' }
});On duplicate key update
MySQL์ ON CONFLICT ์ ๋์ ON DUPLICATE KEY UPDATE๋ฅผ ์ง์ํฉ๋๋ค. MySQL์ ๊ธฐ๋ณธ ํค์ ๊ณ ์ ์ธ๋ฑ์ค๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ถฉ๋ ๋์์ ์๋์ผ๋ก ๊ฒฐ์ ํ๋ฉฐ, ๋ชจ๋ ๊ณ ์ ์ธ๋ฑ์ค ์ถฉ๋ ์ ํ์ ์
๋ฐ์ดํธํฉ๋๋ค.
Drizzle์ onDuplicateKeyUpdate ๋ฉ์๋๋ฅผ ํตํด ์ด๋ฅผ ์ง์ํฉ๋๋ค:
// Note that MySQL automatically determines targets based on the primary key and unique indexes
await db.insert(users)
.values({ id: 1, name: 'John' })
.onDuplicateKeyUpdate({ set: { name: 'John' } });MySQL์ ์ถฉ๋ ์ ์๋ฌด ์์ ๋ ์ํํ์ง ์๋ ๊ฒ์ ์ง์ ์ง์ํ์ง ์์ง๋ง, ์์์ ์ปฌ๋ผ ๊ฐ์ ์์ ์ผ๋ก ์ค์ ํ์ฌ no-op์ ์ํํ๊ณ ๋์ผํ ํจ๊ณผ๋ฅผ ์ป์ ์ ์์ต๋๋ค:
import { sql } from 'drizzle-orm';
await db.insert(users)
.values({ id: 1, name: 'John' })
.onDuplicateKeyUpdate({ set: { id: sql`id` } });with insert ์
with ์ ์ ์ฌ์ฉํ๋ฉด ๋ณต์กํ ์ฟผ๋ฆฌ๋ฅผ ๊ณตํต ํ
์ด๋ธ ํํ์(CTE)์ด๋ผ๊ณ ํ๋ ๋ ์์ ํ์ ์ฟผ๋ฆฌ๋ก ๋ถํ ํ์ฌ ๋จ์ํํ ์ ์์ต๋๋ค:
const userCount = db.$with('user_count').as(
db.select({ value: sql`count(*)`.as('value') }).from(users)
);
const result = await db.with(userCount)
.insert(users)
.values([
{ username: 'user1', admin: sql`((select * from ${userCount}) = 0)` }
])
.returning({
admin: users.admin
});with "user_count" as (select count(*) as "value" from "users")
insert into "users" ("username", "admin")
values ($1, ((select * from "user_count") = 0))
returning "admin"Insert into โฆ select
SQLite ๋ฌธ์์์ ์ธ๊ธํ๋ฏ์ด:
INSERT ๋ฌธ์ ๋ ๋ฒ์งธ ํ์์ VALUES ์ ๋์ SELECT ๋ฌธ์ ํฌํจํฉ๋๋ค. SELECT ๋ฌธ ์คํ์ผ๋ก ๋ฐํ๋ ๊ฐ ๋ฐ์ดํฐ ํ์ ๋ํด ์ ํญ๋ชฉ์ด ํ ์ด๋ธ์ ์ฝ์ ๋ฉ๋๋ค. column-list๊ฐ ์ง์ ๋ ๊ฒฝ์ฐ, SELECT ๊ฒฐ๊ณผ์ ์ปฌ๋ผ ์๋ column-list์ ํญ๋ชฉ ์์ ๊ฐ์์ผ ํฉ๋๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด, column-list๊ฐ ์ง์ ๋์ง ์์ ๊ฒฝ์ฐ SELECT ๊ฒฐ๊ณผ์ ์ปฌ๋ผ ์๋ ํ ์ด๋ธ์ ์ปฌ๋ผ ์์ ๊ฐ์์ผ ํฉ๋๋ค. ๋ณตํฉ SELECT ๋ฐ ORDER BY ๋ฐ/๋๋ LIMIT ์ ์ด ์๋ SELECT ๋ฌธ์ ํฌํจํ ๋ชจ๋ SELECT ๋ฌธ์ ์ด ํ์์ INSERT ๋ฌธ์์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
ํ์ฑ ๋ชจํธ์ฑ์ ํผํ๊ธฐ ์ํด, upsert ์ ์ด ์๋ ๊ฒฝ์ฐ SELECT ๋ฌธ์ ํญ์ WHERE ์ ์ ํฌํจํด์ผ ํ๋ฉฐ, ๋จ์ํ โWHERE trueโ์ธ ๊ฒฝ์ฐ์๋ ๋ง์ฐฌ๊ฐ์ง์ ๋๋ค. WHERE ์ ์ด ์์ผ๋ฉด ํ์๋ ํ ํฐ โONโ์ด SELECT์ ์กฐ์ธ ์ ์ฝ ์กฐ๊ฑด์ ์ผ๋ถ์ธ์ง upsert ์ ์ ์์์ธ์ง ์ ์ ์์ต๋๋ค.
PostgreSQL ๋ฌธ์์์ ์ธ๊ธํ๋ฏ์ด:
์ฝ์ ํ ํ์ ์ ๊ณตํ๋ ์ฟผ๋ฆฌ(SELECT ๋ฌธ)
๊ทธ๋ฆฌ๊ณ MySQL ๋ฌธ์์์ ์ธ๊ธํ๋ฏ์ด:
INSERT โฆ SELECT๋ฅผ ์ฌ์ฉํ๋ฉด ํ๋ ๋๋ ์ฌ๋ฌ ํ ์ด๋ธ์์ ์ ํํ ์ ์๋ SELECT ๋ฌธ์ ๊ฒฐ๊ณผ๋ก๋ถํฐ ํ ์ด๋ธ์ ๋ง์ ํ์ ๋น ๋ฅด๊ฒ ์ฝ์ ํ ์ ์์ต๋๋ค
Drizzle์ ๋ชจ๋ dialect์ ๋ํ ํ์ฌ ๊ตฌ๋ฌธ์ ์ง์ํ๋ฉฐ, ๋ชจ๋ ๋์ผํ ๊ตฌ๋ฌธ์ ๊ณต์ ํฉ๋๋ค. ๋ช ๊ฐ์ง ์ผ๋ฐ์ ์ธ ์๋๋ฆฌ์ค์ API ์ฌ์ฉ๋ฒ์ ๊ฒํ ํด ๋ณด๊ฒ ์ต๋๋ค. insert ๋ฌธ ๋ด์์ select๋ฅผ ์ฌ์ฉํ๋ ์ฌ๋ฌ ๊ฐ์ง ๋ฐฉ๋ฒ์ด ์์ด ์ ํธํ๋ ๋ฐฉ์์ ์ ํํ ์ ์์ต๋๋ค:
- select ํจ์ ๋ด๋ถ์ ์ฟผ๋ฆฌ ๋น๋๋ฅผ ์ ๋ฌํ ์ ์์ต๋๋ค.
- ์ฝ๋ฐฑ ๋ด๋ถ์์ ์ฟผ๋ฆฌ ๋น๋๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
- ์ฌ์ฉํ๋ ค๋ ์ฌ์ฉ์ ์ ์ select ์ฟผ๋ฆฌ์ ํจ๊ป SQL ํ ํ๋ฆฟ ํ๊ทธ๋ฅผ ์ ๋ฌํ ์ ์์ต๋๋ค
const insertedEmployees = await db
.insert(employees)
.select(
db.select({ name: users.name }).from(users).where(eq(users.role, 'employee'))
)
.returning({
id: employees.id,
name: employees.name
});const qb = new QueryBuilder();
await db.insert(employees).select(
qb.select({ name: users.name }).from(users).where(eq(users.role, 'employee'))
);