SQL 插入

Drizzle ORM 为您提供最类似 SQL 的方式来向数据库表中插入行。

插入单行

使用 Drizzle 插入数据非常简单且类似 SQL。请亲自尝试:

await db.insert(users).values({ name: 'Andrew' });
insert into "users" ("name") values ("Andrew");

如果您需要特定表的插入类型,可以使用 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);

插入并返回

PostgreSQL
SQLite
MySQL
SingleStore

您可以在 PostgreSQL 和 SQLite 中像这样插入一行并获取返回的数据:

await db.insert(users).values({ name: "Dan" }).returning();

// partial return
await db.insert(users).values({ name: "Partial Dan" }).returning({ insertedId: users.id });

插入 $returningId

PostgreSQL
SQLite
MySQL
SingleStore

MySQL 本身在使用 INSERT 后不支持原生的 RETURNING。对于带有 autoincrement (或 serial) 类型的 primary keys,只有一种方法可以实现,即您可以通过 insertIdaffectedRows 字段访问。我们为您准备了一种自动化的方式,以便使用 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 }[]

如果没有主键 -> 此类查询的类型将为 {}[]

插入多行

await db.insert(users).values([{ name: 'Andrew' }, { name: 'Dan' }]);

插入或更新与冲突

Drizzle ORM 提供了用于处理插入或更新以及冲突的简单接口。

冲突时不做任何操作

PostgreSQL
SQLite
MySQL
SingleStore

如果发生冲突,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 });

冲突时更新

PostgreSQL
SQLite
MySQL

如果发生冲突,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 中指定这些条件,您可以使用 setWheretargetWhere 子句。

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 的复合主键

await db.insert(users)
  .values({ firstName: 'John', lastName: 'Doe' })
  .onConflictDoUpdate({
    target: [users.firstName, users.lastName],
    set: { firstName: 'John1' }
  });

重复键更新

PostgreSQL
SQLite
MySQL
SingleStore

MySQL 支持 ON DUPLICATE KEY UPDATE,而不是 ON CONFLICT 子句。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 不直接支持冲突时不做任何操作,但您可以通过将任何列的值设置为其本身来执行无操作,并达到相同的效果。

import { sql } from 'drizzle-orm';

await db.insert(users)
  .values({ id: 1, name: 'John' })
  .onDuplicateKeyUpdate({ set: { id: sql`id` } });

insert 子句

请查看如何将 WITH 语句与 selectupdatedelete 结合使用。

使用 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"

插入到…选择

正如 SQLite 文档所述:

INSERT 语句的第二种形式包含 SELECT 语句而不是 VALUES 子句。通过执行 SELECT 语句返回的每行数据都将插入到表中。如果指定了列列表,则 SELECT 结果中的列数必须与列列表中的项目数相同。否则,如果未指定列列表,则 SELECT 结果中的列数必须与表中的列数相同。任何 SELECT 语句,包括复合 SELECT 和带有 ORDER BY 和/或 LIMIT 子句的 SELECT 语句,都可以以这种形式用于 INSERT 语句。

重要提示

为了避免解析歧义,如果存在 upsert 子句,SELECT 语句应始终包含 WHERE 子句,即使该子句仅仅是“WHERE true”。如果没有 WHERE 子句,解析器将不知道令牌“ON”是 SELECT 上连接约束的一部分,还是 upsert 子句的开头。

正如 PostgreSQL 文档所述:

一个提供要插入行的查询(SELECT 语句)

正如 MySQL 文档所述:

使用 INSERT … SELECT,您可以根据 SELECT 语句的结果快速将多行插入到表中,该 SELECT 语句可以从一个或多个表中选择。

Drizzle 支持所有方言的当前语法,并且它们都共享相同的语法。让我们回顾一些常见场景和 API 用法。有几种方法可以在插入语句中使用 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'))
);