์ฟผ๋ฆฌ ๋ด๋ถ์ ๋ชจ๋ ๋งค๊ฐ๋ณ์๋ $1, $2 ๋ฑ์ผ๋ก ๋์ฒด๋์ง ์๊ณ ์ธ๋ผ์ธ ์ฒ๋ฆฌ๋ฉ๋๋ค.
๋ทฐ
Drizzle ORM์ผ๋ก ๋ทฐ๋ฅผ ์ ์ธํ๋ ๋ฐฉ๋ฒ์ ์ฌ๋ฌ ๊ฐ์ง๊ฐ ์์ต๋๋ค.
์์ฑํด์ผ ํ๋ ๋ทฐ๋ฅผ ์ ์ธํ๊ฑฐ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ด๋ฏธ ์กด์ฌํ๋ ๋ทฐ๋ฅผ ์ ์ธํ ์ ์์ต๋๋ค.
์ธ๋ผ์ธ query builder ๊ตฌ๋ฌธ, ๋
๋ฆฝํ query builder ๋ฐ ์์ sql ์ฐ์ฐ์๋ก ๋ทฐ ๋ฌธ์ ์ ์ธํ ์ ์์ต๋๋ค.
์ธ๋ผ์ธ ๋๋ ๋
๋ฆฝํ ์ฟผ๋ฆฌ ๋น๋๋ก ๋ทฐ๋ฅผ ์์ฑํ๋ฉด ๋ทฐ ์ปฌ๋ผ ์คํค๋ง๊ฐ ์๋์ผ๋ก ์ถ๋ก ๋ฉ๋๋ค.
๊ทธ๋ฌ๋ sql์ ์ฌ์ฉํ ๋๋ ๋ทฐ ์ปฌ๋ผ ์คํค๋ง๋ฅผ ๋ช
์์ ์ผ๋ก ์ ์ธํด์ผ ํฉ๋๋ค.
๋ทฐ ์ ์ธํ๊ธฐ
import { pgTable, pgView, serial, text, timestamp } from "drizzle-orm/pg-core";
export const user = pgTable("user", {
id: serial(),
name: text(),
email: text(),
password: text(),
role: text().$type<"admin" | "customer">(),
createdAt: timestamp("created_at"),
updatedAt: timestamp("updated_at"),
});
export const userView = pgView("user_view").as((qb) => qb.select().from(user));
export const customersView = pgView("customers_view").as((qb) => qb.select().from(user).where(eq(user.role, "customer")));CREATE VIEW "user_view" AS SELECT * FROM "user";
CREATE VIEW "customers_view" AS SELECT * FROM "user" WHERE "role" = 'customer';์ปฌ๋ผ์ ํ์ ์งํฉ์ด ํ์ํ ๊ฒฝ์ฐ ๋ค์๊ณผ ๊ฐ์ด ์ฟผ๋ฆฌ ๋น๋์์ .select({ ... }) ๋ฉ์๋๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค:
export const customersView = pgView("customers_view").as((qb) => {
return qb
.select({
id: user.id,
name: user.name,
email: user.email,
})
.from(user);
});CREATE VIEW "customers_view" AS SELECT "id", "name", "email" FROM "user" WHERE "role" = 'customer';๋
๋ฆฝํ query builder๋ฅผ ์ฌ์ฉํ์ฌ ๋ทฐ๋ฅผ ์ ์ธํ ์๋ ์์ผ๋ฉฐ, ๋์ผํ ๋ฐฉ์์ผ๋ก ์๋ํฉ๋๋ค:
import { pgTable, pgView, serial, text, timestamp, QueryBuilder} from "drizzle-orm/pg-core";
const qb = new QueryBuilder();
export const user = pgTable("user", {
id: serial(),
name: text(),
email: text(),
password: text(),
role: text().$type<"admin" | "customer">(),
createdAt: timestamp("created_at"),
updatedAt: timestamp("updated_at"),
});
export const userView = pgView("user_view").as(qb.select().from(user));
export const customersView = pgView("customers_view").as(qb.select().from(user).where(eq(user.role, "customer")));CREATE VIEW "user_view" AS SELECT * FROM "user";
CREATE VIEW "customers_view" AS SELECT * FROM "user" WHERE "role" = 'customer';์์ SQL๋ก ๋ทฐ ์ ์ธํ๊ธฐ
์ฟผ๋ฆฌ ๋น๋์์ ์ง์ํ์ง ์๋ ๊ตฌ๋ฌธ์ ์ฌ์ฉํ์ฌ ๋ทฐ๋ฅผ ์ ์ธํด์ผ ํ๋ ๊ฒฝ์ฐ,
sql ์ฐ์ฐ์๋ฅผ ์ง์ ์ฌ์ฉํ๊ณ ๋ทฐ ์ปฌ๋ผ ์คํค๋ง๋ฅผ ๋ช
์์ ์ผ๋ก ์ง์ ํ ์ ์์ต๋๋ค.
// ์ผ๋ฐ ๋ทฐ
const newYorkers = pgView('new_yorkers', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
cityId: integer('city_id').notNull(),
}).as(sql`select * from ${users} where ${eq(users.cityId, 1)}`);
// ๊ตฌ์ฒดํ๋ ๋ทฐ
const newYorkers = pgMaterializedView('new_yorkers', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
cityId: integer('city_id').notNull(),
}).as(sql`select * from ${users} where ${eq(users.cityId, 1)}`);๊ธฐ์กด ๋ทฐ ์ ์ธํ๊ธฐ
๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๊ธฐ์กด ๋ทฐ์ ๋ํ ์ฝ๊ธฐ ์ ์ฉ ์ก์ธ์ค๊ฐ ์ ๊ณต๋๋ ๊ฒฝ์ฐ .existing() ๋ทฐ ๊ตฌ์ฑ์ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
drizzle-kit์ ์์ฑ๋ ๋ง์ด๊ทธ๋ ์ด์
์์ create view ๋ฌธ์ ๋ฌด์ํ๊ณ ์์ฑํ์ง ์์ต๋๋ค.
export const user = pgTable("user", {
id: serial(),
name: text(),
email: text(),
password: text(),
role: text().$type<"admin" | "customer">(),
createdAt: timestamp("created_at"),
updatedAt: timestamp("updated_at"),
});
// ์ผ๋ฐ ๋ทฐ
export const trimmedUser = pgView("trimmed_user", {
id: serial("id"),
name: text("name"),
email: text("email"),
}).existing();
// ๊ตฌ์ฒดํ๋ ๋ทฐ๋ ์ฐจ์ด๊ฐ ์์ง๋ง ์ผ๊ด์ฑ์ ์ํด ์ฌ์ฉํ ์ ์์ต๋๋ค
export const trimmedUser = pgMaterializedView("trimmed_user", {
id: serial("id"),
name: text("name"),
email: text("email"),
}).existing();๊ตฌ์ฒดํ๋ ๋ทฐ
๊ณต์ ๋ฌธ์์ ๋ฐ๋ฅด๋ฉด PostgreSQL์ ์ผ๋ฐ
๋ฐ ๊ตฌ์ฒดํ๋ ๋ทฐ๋ฅผ ๋ชจ๋ ์ ๊ณตํฉ๋๋ค.
PostgreSQL์ ๊ตฌ์ฒดํ๋ ๋ทฐ๋ ์ผ๋ฐ ๋ทฐ์ ๊ฐ์ด ๊ท์น ์์คํ ์ ์ฌ์ฉํ์ง๋ง ๊ฒฐ๊ณผ๋ฅผ ํ ์ด๋ธ๊ณผ ๊ฐ์ ํํ๋ก ์ ์งํฉ๋๋ค.
Drizzle ORM์ PostgreSQL ๊ตฌ์ฒดํ๋ ๋ทฐ๋ฅผ ๊ธฐ๋ณธ์ ์ผ๋ก ์ง์ํฉ๋๋ค:
const newYorkers = pgMaterializedView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1)));CREATE MATERIALIZED VIEW "new_yorkers" AS SELECT * FROM "users";์ ํ๋ฆฌ์ผ์ด์ ๋ฐํ์์์ ๊ตฌ์ฒดํ๋ ๋ทฐ๋ฅผ ์๋ก ๊ณ ์น ์ ์์ต๋๋ค:
await db.refreshMaterializedView(newYorkers);
await db.refreshMaterializedView(newYorkers).concurrently();
await db.refreshMaterializedView(newYorkers).withNoData();ํ์ฅ๋ ์์
// ์ผ๋ฐ ๋ทฐ
const newYorkers = pgView('new_yorkers')
.with({
checkOption: 'cascaded',
securityBarrier: true,
securityInvoker: true,
})
.as((qb) => {
const sq = qb
.$with('sq')
.as(
qb.select({ userId: users.id, cityId: cities.id })
.from(users)
.leftJoin(cities, eq(cities.id, users.homeCity))
.where(sql`${users.age1} > 18`),
);
return qb.with(sq).select().from(sq).where(sql`${users.homeCity} = 1`);
});
// ๊ตฌ์ฒดํ๋ ๋ทฐ
const newYorkers2 = pgMaterializedView('new_yorkers')
.using('btree')
.with({
fillfactor: 90,
toast_tuple_target: 0.5,
autovacuum_enabled: true,
...
})
.tablespace('custom_tablespace')
.withNoData()
.as((qb) => {
const sq = qb
.$with('sq')
.as(
qb.select({ userId: users.id, cityId: cities.id })
.from(users)
.leftJoin(cities, eq(cities.id, users.homeCity))
.where(sql`${users.age1} > 18`),
);
return qb.with(sq).select().from(sq).where(sql`${users.homeCity} = 1`);
});