生成的列

要使用此功能,你需要拥有 drizzle-orm@0.32.0 或更高版本以及 drizzle-kit@0.23.0 或更高版本

SQL 中的生成列功能允许你在表中创建列,其值会根据涉及同一表中其他列的表达式自动计算。这有助于确保数据一致性,简化数据库设计,并提高查询性能。

生成的列有两种类型:

  1. 虚拟(或非持久)生成列:这些列在每次查询时都会动态计算。它们不占用数据库的存储空间。

  2. 存储(或持久化)生成的列:这些列在插入或更新行时计算,并将其值存储在数据库中。这使得它们可以被索引,并且可以提高查询性能,因为无需为每个查询重新计算值。

生成的列尤其适用于:

生成的列的实现和使用方式在不同的 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
);

callback - 如果你需要引用表中的列

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<{ data: string }>({
  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) => [
    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");