생성된 컬럼(Generated Columns)

이 기능을 사용하려면 drizzle-orm@0.32.0 이상과 drizzle-kit@0.23.0 이상 버전이 필요합니다.

SQL에서 생성된 컬럼은 동일한 테이블 내의 다른 컬럼을 기반으로 자동으로 계산된 값을 가지는 컬럼을 생성할 수 있는 기능입니다. 이를 통해 데이터 일관성을 보장하고, 데이터베이스 설계를 단순화하며, 쿼리 성능을 향상시킬 수 있습니다.

생성된 컬럼에는 두 가지 유형이 있습니다:

  1. 가상(Virtual) 생성된 컬럼: 쿼리할 때마다 동적으로 계산됩니다. 데이터베이스 저장 공간을 차지하지 않습니다.

  2. 저장(Stored) 생성된 컬럼: 행이 삽입되거나 업데이트될 때 계산되며, 그 값은 데이터베이스에 저장됩니다. 이로 인해 인덱싱이 가능하며, 쿼리 성능이 향상됩니다.

생성된 컬럼은 다음과 같은 경우에 특히 유용합니다:

생성된 컬럼의 구현과 사용법은 SQL 데이터베이스마다 크게 다를 수 있습니다. PostgreSQL, MySQL, SQLite는 각각 고유한 기능, 능력, 제한 사항을 가지고 있습니다. 이 섹션에서는 각 데이터베이스 시스템에서 생성된 컬럼을 최적으로 활용하는 방법을 자세히 살펴보겠습니다.

PostgreSQL
MySQL
SQLite
SingleStore(WIP)

데이터베이스 측면

유형: STORED만 지원

동작 방식

  • 삽입 또는 업데이트 시 다른 컬럼을 기반으로 값을 자동으로 계산합니다.

기능

  • 복잡한 표현식을 미리 계산하여 데이터 접근을 단순화합니다.
  • 생성된 컬럼에 인덱스를 지원하여 쿼리 성능을 향상시킵니다.

제한 사항

  • 기본값을 지정할 수 없습니다.
  • 표현식이 다른 생성된 컬럼을 참조하거나 서브쿼리를 포함할 수 없습니다.
  • 생성된 컬럼 표현식을 수정하려면 스키마 변경이 필요합니다.
  • 기본 키, 외래 키, 고유 제약 조건에 직접 사용할 수 없습니다.

자세한 내용은 PostgreSQL 문서를 참조하세요.

Drizzle 측면

Drizzle에서는 모든 컬럼 타입에 .generatedAlwaysAs() 함수를 지정하고 지원되는 SQL 쿼리를 추가하여 이 컬럼 데이터를 생성할 수 있습니다.

기능

이 함수는 세 가지 방식으로 생성된 표현식을 받을 수 있습니다:

string

export const test = pgTable("test", {
    generatedName: text("gen_name").generatedAlwaysAs(`hello world!`),
});
CREATE TABLE IF NOT EXISTS "test" (
    "gen_name" text GENERATED ALWAYS AS (hello world!) STORED
);

sql 태그 - Drizzle가 일부 값을 이스케이프하도록 할 때

export const test = pgTable("test", {
    generatedName: text("gen_name").generatedAlwaysAs(sql`hello "world"!`),
});
CREATE TABLE IF NOT EXISTS "test" (
    "gen_name" text GENERATED ALWAYS AS (hello "world"!) STORED,
);

콜백 - 테이블의 컬럼을 참조해야 할 때

export const test = pgTable("test", {
    name: text("first_name"),
    generatedName: text("gen_name").generatedAlwaysAs(
      (): SQL => sql`hi, ${test.name}!`
    ),
});
CREATE TABLE IF NOT EXISTS "test" (
    "first_name" text,
    "gen_name" text GENERATED ALWAYS AS (hi, "test"."first_name"!) STORED,
);

예제 전체 텍스트 검색과 함께 생성된 컬럼

schema.ts
import { SQL, sql } from "drizzle-orm";
import { customType, index, integer, pgTable, text } from "drizzle-orm/pg-core";

const tsVector = customType({
  dataType() {
    return "tsvector";
  },
});

export const test = pgTable(
  "test",
  {
    id: integer("id").primaryKey().generatedAlwaysAsIdentity(),
    content: text("content"),
    contentSearch: tsVector("content_search", {
      dimensions: 3,
    }).generatedAlwaysAs(
      (): SQL => sql`to_tsvector('english', ${test.content})`
    ),
  },
  (t) => ({
    idx: index("idx_content_search").using("gin", t.contentSearch),
  })
);
CREATE TABLE IF NOT EXISTS "test" (
	"id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "test_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),
	"content" text,
	"content_search" "tsvector" GENERATED ALWAYS AS (to_tsvector('english', "test"."content")) STORED
);
--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "idx_content_search" ON "test" USING gin ("content_search");