쿼리 내부의 모든 매개변수는 $1
, $2
등으로 대체되지 않고 그대로 인라인 처리됩니다.
뷰(Views)
Drizzle ORM을 사용하면 여러 가지 방법으로 뷰를 선언할 수 있습니다.
데이터베이스에 생성해야 하는 뷰를 선언하거나, 이미 데이터베이스에 존재하는 뷰를 선언할 수 있습니다.
뷰 선언은 쿼리 빌더
문법을 인라인으로 사용하거나, 독립형 쿼리 빌더
를 사용하거나, 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(),
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';
또한, standalone 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(),
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';
Raw 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)}`);
// 구체화된 뷰(materialized view)
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(),
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();
구체화된 뷰(Materialized Views)
공식 문서에 따르면, 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`), // 나이가 18세 이상인 사용자 선택
);
return qb.with(sq).select().from(sq).where(sql`${users.homeCity} = 1`); // 홈 도시가 1인 사용자 선택
});
// 구체화된 뷰
const newYorkers2 = pgMaterializedView('new_yorkers')
.using('btree') // B-트리 인덱스 사용
.with({
fillfactor: 90, // 채우기 비율 90%
toast_tuple_target: 0.5, // TOAST 튜플 목표치 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`), // 나이가 18세 이상인 사용자 선택
);
return qb.with(sq).select().from(sq).where(sql`${users.homeCity} = 1`); // 홈 도시가 1인 사용자 선택
});