4.1 insert:新增行
import { db } from "./db";
import { users } from "./schema";
// 插入一行
await db.insert(users).values({
name: "Alice",
email: "alice@example.com",
});
// 批量插入
await db.insert(users).values([
{ name: "Bob", email: "bob@example.com" },
{ name: "Carol", email: "carol@example.com" },
]);
returning:拿到刚插入的行
// PG/SQLite 支持 RETURNING
const [inserted] = await db
.insert(users)
.values({ name: "Dan", email: "dan@x.com" })
.returning();
console.log(inserted.id); // 新生成的 id
// 只取部分字段
const rows = await db.insert(users)
.values([{ name: "E", email: "e@x.com" }])
.returning({ id: users.id, email: users.email });
MySQL 不支持 RETURNING
MySQL 8.x 没有 RETURNING 子句——Drizzle 的 mysql2 版本 .returning() 被禁用。想拿 id 用 result.lastInsertId 或插入后单独查询。
4.2 select:查询
// 全表所有列
const all = await db.select().from(users);
// partial select:只要某些列
const names = await db
.select({ id: users.id, name: users.name })
.from(users);
// where
import { eq, gt, and } from "drizzle-orm";
const adults = await db
.select()
.from(users)
.where(gt(users.age, 18));
// limit / offset / orderBy
import { desc } from "drizzle-orm";
const top10 = await db
.select()
.from(users)
.orderBy(desc(users.createdAt))
.limit(10)
.offset(20);
4.3 类型推断实战
const rows = await db.select().from(users);
// rows: { id: number; name: string; email: string; age: number | null; ... }[]
const partial = await db.select({ id: users.id, name: users.name }).from(users);
// partial: { id: number; name: string }[] —— 只含你选的字段
4.4 update
import { eq } from "drizzle-orm";
await db
.update(users)
.set({ name: "Alice Renamed", age: 31 })
.where(eq(users.id, 1));
// 用现有列值计算新值
import { sql } from "drizzle-orm";
await db
.update(posts)
.set({ viewCount: sql`${posts.viewCount} + 1` })
.where(eq(posts.id, id));
// returning
const [updated] = await db
.update(users).set({ age: 40 })
.where(eq(users.id, 1))
.returning();
永远写 where!
db.update(users).set({ age: 0 }) 不加 where 会把全表所有行的 age 改成 0。Drizzle 不给你安全网——SQL 是什么语义就是什么语义。Code review 时尤其留意 update/delete 调用链是否缺 where。
4.5 delete
await db.delete(users).where(eq(users.id, 1));
// returning 删除的行
const deleted = await db
.delete(users)
.where(lt(users.createdAt, new Date("2020-01-01")))
.returning();
4.6 onConflict / upsert
插入时若唯一键冲突,怎么办?三种策略:
Postgres / SQLite:onConflictDoUpdate
await db.insert(users)
.values({ email: "alice@x.com", name: "Alice", age: 31 })
.onConflictDoUpdate({
target: users.email, // 冲突检测的列(唯一约束)
set: {
name: "Alice",
age: 31,
// 用 excluded 引用"将要插入但被阻止的那行"
updatedAt: sql`now()`,
},
// 可选:只在满足条件时才更新
// where: gt(users.age, 0),
});
onConflictDoNothing
await db.insert(users)
.values({ email: "alice@x.com", name: "Alice" })
.onConflictDoNothing({ target: users.email });
MySQL:onDuplicateKeyUpdate
await db.insert(users)
.values({ email: "alice@x.com", name: "Alice" })
.onDuplicateKeyUpdate({
set: { name: "Alice" },
});
4.7 批量插入性能
// Drizzle 会生成一条多值 INSERT,比循环单条快几十倍
const items = Array.from({ length: 1000 }, (_, i) => ({
name: `u${i}`,
email: `u${i}@x.com`,
}));
await db.insert(users).values(items);
超大批量(> 10k 行)建议分批 1000-5000 一次,避免单条 SQL 过长。
4.8 prepared statement(预编译)
同一条查询要跑很多次时,预编译省重复解析:
import { sql } from "drizzle-orm";
const findByEmail = db
.select().from(users)
.where(eq(users.email, sql.placeholder("email")))
.prepare("find_user_by_email"); // 给准备语句命名(PG)
const u1 = await findByEmail.execute({ email: "a@x.com" });
const u2 = await findByEmail.execute({ email: "b@x.com" });
4.9 $returning / $inferInsert 类型玩法
type NewUser = typeof users.$inferInsert;
async function createUser(data: NewUser) {
const [u] = await db.insert(users).values(data).returning();
return u;
}
// 参数类型自动不含 id/createdAt(可选),返回类型是 User 完整对象
4.10 小结
- 四件套 API:
db.insert(t).values()/select().from().where()/update(t).set().where()/delete(t).where()。 - Postgres/SQLite 支持
.returning()拿到刚插入/更新/删除的行;MySQL 不支持。 - upsert:
onConflictDoUpdate(PG/SQLite)/onDuplicateKeyUpdate(MySQL)。 - update/delete 永远记得 where——Drizzle 不给安全网。
- 批量插入直接
.values([...]),Drizzle 生成多值 INSERT。 - 热路径用
.prepare()预编译。