SQL Insert

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

您可以在 PostgreSQL 和 SQLite 中像这样插入一行并获取它:

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

// 部分返回
await db.insert(users).values({ name: "Partial Dan" }).returning({ insertedId: users.id });

Insert $returningId

PostgreSQL
SQLite
MySQL

MySQL itself doesn’t have native support for RETURNING after using INSERT. There is only one way to do it for primary keys with autoincrement (or serial) types, where you can access insertId and affectedRows fields. We’ve prepared an automatic way for you to handle such cases with Drizzle and automatically receive all inserted IDs as separate objects

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 }[]

Also with Drizzle, you can specify a primary key with $default function that will generate custom primary keys at runtime. We will also return those generated keys for you in the $returningId() call

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 }[]

If there is no primary keys -> type will be {}[] for such queries

Insert $returningId

PostgreSQL
SQLite
MySQL

MySQL itself doesn’t have native support for RETURNING after using INSERT. There is only one way to do it for primary keys with autoincrement (or serial) types, where you can access insertId and affectedRows fields. We’ve prepared an automatic way for you to handle such cases with Drizzle and automatically receive all inserted IDs as separate objects

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 }[]

Also with Drizzle, you can specify a primary key with $default function that will generate custom primary keys at runtime. We will also return those generated keys for you in the $returningId() call

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 }[]

If there is no primary keys -> type will be {}[] for such queries

插入多行

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

Upserts 和冲突

Drizzle ORM 提供了处理 Upserts 和冲突的简单接口。

冲突时不做任何操作

PostgreSQL
SQLite
MySQL

onConflictDoNothing 如果存在冲突将取消插入:

await db.insert(users)
  .values({ id: 1, name: 'John' })
  .onConflictDoNothing();

// 明确指定冲突目标
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'`
  });

使用复合索引的 Upsert,或者 onConflictDoUpdate 的复合主键:

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

重复键更新

PostgreSQL
SQLite
MySQL

MySQL 支持 ON DUPLICATE KEY UPDATE 而不是 ON CONFLICT 子句。MySQL 将自动根据主键和唯一索引确定冲突目标,如果存在任何唯一索引冲突,将更新行。

Drizzle 通过 onDuplicateKeyUpdate 方法支持此功能:

// 注意 MySQL 会根据主键和唯一索引自动确定目标
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` } });

WITH INSERT 子句

💡

查看如何使用 WITH 语句与 selectupdatedelete

使用 with 子句可以帮助您通过将复杂查询拆分为称为公共表表达式(CTEs)的较小子查询来简化复杂查询:

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"
Become a Gold Sponsor