better-drizzle

Update many rows with different values

updateEach updates many rows, each with its own values, in one UPDATE ... CASE statement.

Use updateEach when many rows need different values (a sorted list, prices from a feed, names from an import). It generates one CASE expression per column instead of one UPDATE per row. When every row gets the same value, use updateMany.

const { count } = await client.users.updateEach({
	by: users.id,
	data: [
		{ id: 1, name: 'Alice Updated', city: 'New York' },
		{ id: 2, name: 'Bob Updated', city: 'Chicago' },
	],
	update: {
		name: (row) => row.name,
		city: (row) => row.city,
	},
});

It runs a single statement:

UPDATE users
SET
  name = CASE WHEN id = 1 THEN 'Alice Updated' WHEN id = 2 THEN 'Bob Updated' ELSE name END,
  city = CASE WHEN id = 1 THEN 'New York' WHEN id = 2 THEN 'Chicago' ELSE city END
WHERE id IN (1, 2)

by is a Drizzle column instance (users.id), not a column key. Each data row must carry its by value.

Limit and return the updated rows

where is combined with the generated by IN (...) predicate, so only eligible rows change. select returns scalar columns of the updated rows:

const { data } = await client.users.updateEach({
	by: users.id,
	data: [
		{ id: 1, name: 'Alice' },
		{ id: 2, name: 'Bob' },
	],
	update: { name: (row) => row.name },
	where: { active: true }, // only active users
	select: { id: true, name: true, email: true },
});

Compute values in SQL

A callback can return a Drizzle SQL value. It goes into that row's CASE branch as is, so it can call SQL functions or reference columns:

import { sql } from 'drizzle-orm';

const { count } = await client.users.updateEach({
	by: users.id,
	data: [
		{ id: 1, name: 'alice' },
		{ id: 2, name: 'bob' },
	],
	update: {
		name: (row) => sql`upper(${row.name})`,
		city: () => sql`coalesce(${users.city}, 'Unknown')`,
	},
});

Callbacks also accept atomic updates, array updates, and JSONB path updates.

Edge cases

Reference

OptionTypeRequiredDescription
byTableColumnYesDrizzle column instance used to match rows (e.g., users.id)
dataRow[]YesSource rows containing the by value plus any extra fields
updateobjectYesPer-column callbacks (row) => value | SQL that resolve each row's new value
whereWhereArgNoExtra filter combined with the generated by IN (...) predicate
selectSelectInputNoScalar projection for returned rows
onEmpty'return' | 'throw'NoBehavior when data is empty. Defaults to 'return'
metaMetaNoCustom metadata forwarded to hooks

Returns Promise<BatchResult<T>>: { count, data? }, with data when select is set and the driver supports RETURNING.

Use updateMany whenUse updateEach when
All matching rows get the same valueEach row needs a different value
A WHERE filter is enoughYou need per-row logic in callbacks

On this page