Skip to main content
Version: v6 - stable

模型查询 - 基础

Sequelize 提供了各种方法来协助查询数据库中的数据。

¥Sequelize provides various methods to assist querying your database for data.

重要的提醒:要使用 Sequelize 执行生产就绪查询,请确保你也已阅读 事务指南。事务对于确保数据完整性和提供其他好处非常重要。

¥Important notice: to perform production-ready queries with Sequelize, make sure you have read the Transactions guide as well. Transactions are important to ensure data integrity and to provide other benefits.

本指南将展示如何进行标准 CRUD 查询。

¥This guide will show how to make the standard CRUD queries.

简单的 INSERT 查询

¥Simple INSERT queries

首先,一个简单的例子:

¥First, a simple example:

// Create a new user
const jane = await User.create({ firstName: 'Jane', lastName: 'Doe' });
console.log("Jane's auto-generated ID:", jane.id);

Model.create() 方法是使用 Model.build() 构建未保存实例并使用 instance.save() 保存实例的简写。

¥The Model.create() method is a shorthand for building an unsaved instance with Model.build() and saving the instance with instance.save().

还可以定义可以在 create 方法中设置哪些属性。如果你基于可由用户填写的表单创建数据库条目,这会特别有用。例如,使用它可以允许你限制 User 模型仅设置用户名而不设置管理员标志(即 isAdmin):

¥It is also possible to define which attributes can be set in the create method. This can be especially useful if you create database entries based on a form which can be filled by a user. Using that would, for example, allow you to restrict the User model to set only an username but not an admin flag (i.e., isAdmin):

const user = await User.create(
{
username: 'alice123',
isAdmin: true,
},
{ fields: ['username'] },
);
// let's assume the default of isAdmin is false
console.log(user.username); // 'alice123'
console.log(user.isAdmin); // false

简单的 SELECT 查询

¥Simple SELECT queries

你可以使用 findAll 方法从数据库读取整个表:

¥You can read the whole table from the database with the findAll method:

// Find all users
const users = await User.findAll();
console.log(users.every(user => user instanceof User)); // true
console.log('All users:', JSON.stringify(users, null, 2));
SELECT * FROM ...

指定 SELECT 查询的属性

¥Specifying attributes for SELECT queries

要仅选择某些属性,可以使用 attributes 选项:

¥To select only some attributes, you can use the attributes option:

Model.findAll({
attributes: ['foo', 'bar'],
});
SELECT foo, bar FROM ...

可以使用嵌套数组重命名属性:

¥Attributes can be renamed using a nested array:

Model.findAll({
attributes: ['foo', ['bar', 'baz'], 'qux'],
});
SELECT foo, bar AS baz, qux FROM ...

你可以使用 sequelize.fn 进行聚合:

¥You can use sequelize.fn to do aggregations:

Model.findAll({
attributes: ['foo', [sequelize.fn('COUNT', sequelize.col('hats')), 'n_hats'], 'bar'],
});
SELECT foo, COUNT(hats) AS n_hats, bar FROM ...

使用聚合函数时,必须为其指定别名才能从模型访问它。在上面的示例中,你可以通过 instance.n_hats 获取帽子的数量。

¥When using aggregation function, you must give it an alias to be able to access it from the model. In the example above you can get the number of hats with instance.n_hats.

有时,如果你只想添加聚合,则列出模型的所有属性可能会很烦人:

¥Sometimes it may be tiresome to list all the attributes of the model if you only want to add an aggregation:

// This is a tiresome way of getting the number of hats (along with every column)
Model.findAll({
attributes: [
'id',
'foo',
'bar',
'baz',
'qux',
'hats', // We had to list all attributes...
[sequelize.fn('COUNT', sequelize.col('hats')), 'n_hats'], // To add the aggregation...
],
});

// This is shorter, and less error prone because it still works if you add / remove attributes from your model later
Model.findAll({
attributes: {
include: [[sequelize.fn('COUNT', sequelize.col('hats')), 'n_hats']],
},
});
SELECT id, foo, bar, baz, qux, hats, COUNT(hats) AS n_hats FROM ...

同样,也可以删除选定的几个属性:

¥Similarly, it's also possible to remove a selected few attributes:

Model.findAll({
attributes: { exclude: ['baz'] },
});
-- Assuming all columns are 'id', 'foo', 'bar', 'baz' and 'qux'
SELECT id, foo, bar, qux FROM ...

应用 WHERE 子句

¥Applying WHERE clauses

where 选项用于过滤查询。有许多运算符可用于 where 子句,可作为 Op 中的符号使用。

¥The where option is used to filter the query. There are lots of operators to use for the where clause, available as Symbols from Op.

基础

¥The basics

Post.findAll({
where: {
authorId: 2,
},
});
// SELECT * FROM post WHERE authorId = 2;

请注意,没有显式传递任何运算符(来自 Op),因此 Sequelize 默认情况下采用相等比较。上面的代码相当于:

¥Observe that no operator (from Op) was explicitly passed, so Sequelize assumed an equality comparison by default. The above code is equivalent to:

const { Op } = require('sequelize');
Post.findAll({
where: {
authorId: {
[Op.eq]: 2,
},
},
});
// SELECT * FROM post WHERE authorId = 2;

可以通过多项检查:

¥Multiple checks can be passed:

Post.findAll({
where: {
authorId: 12,
status: 'active',
},
});
// SELECT * FROM post WHERE authorId = 12 AND status = 'active';

就像 Sequelize 在第一个示例中推断出 Op.eq 运算符一样,这里 Sequelize 推断出调用者想要 AND 来进行这两项检查。上面的代码相当于:

¥Just like Sequelize inferred the Op.eq operator in the first example, here Sequelize inferred that the caller wanted an AND for the two checks. The code above is equivalent to:

const { Op } = require('sequelize');
Post.findAll({
where: {
[Op.and]: [{ authorId: 12 }, { status: 'active' }],
},
});
// SELECT * FROM post WHERE authorId = 12 AND status = 'active';

OR 可以通过类似的方式轻松执行:

¥An OR can be easily performed in a similar way:

const { Op } = require('sequelize');
Post.findAll({
where: {
[Op.or]: [{ authorId: 12 }, { authorId: 13 }],
},
});
// SELECT * FROM post WHERE authorId = 12 OR authorId = 13;

由于上面是涉及相同字段的 OR,Sequelize 允许你使用稍微不同的结构,该结构更具可读性并生成相同的行为:

¥Since the above was an OR involving the same field, Sequelize allows you to use a slightly different structure which is more readable and generates the same behavior:

const { Op } = require('sequelize');
Post.destroy({
where: {
authorId: {
[Op.or]: [12, 13],
},
},
});
// DELETE FROM post WHERE authorId = 12 OR authorId = 13;

运算符

¥Operators

Sequelize 提供了多个运算符。

¥Sequelize provides several operators.

const { Op } = require("sequelize");
Post.findAll({
where: {
[Op.and]: [{ a: 5 }, { b: 6 }], // (a = 5) AND (b = 6)
[Op.or]: [{ a: 5 }, { b: 6 }], // (a = 5) OR (b = 6)
someAttribute: {
// Basics
[Op.eq]: 3, // = 3
[Op.ne]: 20, // != 20
[Op.is]: null, // IS NULL
[Op.not]: true, // IS NOT TRUE
[Op.or]: [5, 6], // (someAttribute = 5) OR (someAttribute = 6)

// Using dialect specific column identifiers (PG in the following example):
[Op.col]: 'user.organization_id', // = "user"."organization_id"

// Number comparisons
[Op.gt]: 6, // > 6
[Op.gte]: 6, // >= 6
[Op.lt]: 10, // < 10
[Op.lte]: 10, // <= 10
[Op.between]: [6, 10], // BETWEEN 6 AND 10
[Op.notBetween]: [11, 15], // NOT BETWEEN 11 AND 15

// Other operators

[Op.all]: sequelize.literal('SELECT 1'), // > ALL (SELECT 1)

[Op.in]: [1, 2], // IN [1, 2]
[Op.notIn]: [1, 2], // NOT IN [1, 2]

[Op.like]: '%hat', // LIKE '%hat'
[Op.notLike]: '%hat', // NOT LIKE '%hat'
[Op.startsWith]: 'hat', // LIKE 'hat%'
[Op.endsWith]: 'hat', // LIKE '%hat'
[Op.substring]: 'hat', // LIKE '%hat%'
[Op.iLike]: '%hat', // ILIKE '%hat' (case insensitive) (PG only)
[Op.notILike]: '%hat', // NOT ILIKE '%hat' (PG only)
[Op.regexp]: '^[h|a|t]', // REGEXP/~ '^[h|a|t]' (MySQL/PG only)
[Op.notRegexp]: '^[h|a|t]', // NOT REGEXP/!~ '^[h|a|t]' (MySQL/PG only)
[Op.iRegexp]: '^[h|a|t]', // ~* '^[h|a|t]' (PG only)
[Op.notIRegexp]: '^[h|a|t]', // !~* '^[h|a|t]' (PG only)

[Op.any]: [2, 3], // ANY (ARRAY[2, 3]::INTEGER[]) (PG only)
[Op.match]: Sequelize.fn('to_tsquery', 'fat & rat') // match text search for strings 'fat' and 'rat' (PG only)

// In Postgres, Op.like/Op.iLike/Op.notLike can be combined to Op.any:
[Op.like]: { [Op.any]: ['cat', 'hat'] } // LIKE ANY (ARRAY['cat', 'hat'])

// There are more postgres-only range operators, see below
}
}
});

Op.in 的简写语法

¥Shorthand syntax for Op.in

将数组直接传递给 where 选项将隐式使用 IN 运算符:

¥Passing an array directly to the where option will implicitly use the IN operator:

Post.findAll({
where: {
id: [1, 2, 3], // Same as using `id: { [Op.in]: [1,2,3] }`
},
});
// SELECT ... FROM "posts" AS "post" WHERE "post"."id" IN (1, 2, 3);

与运算符的逻辑组合

¥Logical combinations with operators

运算符 Op.andOp.orOp.not 可用于创建任意复杂的嵌套逻辑比较。

¥The operators Op.and, Op.or and Op.not can be used to create arbitrarily complex nested logical comparisons.

Op.andOp.or 的示例

¥Examples with Op.and and Op.or

const { Op } = require("sequelize");

Foo.findAll({
where: {
rank: {
[Op.or]: {
[Op.lt]: 1000,
[Op.eq]: null
}
},
// rank < 1000 OR rank IS NULL

{
createdAt: {
[Op.lt]: new Date(),
[Op.gt]: new Date(new Date() - 24 * 60 * 60 * 1000)
}
},
// createdAt < [timestamp] AND createdAt > [timestamp]

{
[Op.or]: [
{
title: {
[Op.like]: 'Boat%'
}
},
{
description: {
[Op.like]: '%boat%'
}
}
]
}
// title LIKE 'Boat%' OR description LIKE '%boat%'
}
});

Op.not 的示例

¥Examples with Op.not

Project.findAll({
where: {
name: 'Some Project',
[Op.not]: [
{ id: [1, 2, 3] },
{
description: {
[Op.like]: 'Hello%',
},
},
],
},
});

上面将生成:

¥The above will generate:

SELECT *
FROM `Projects`
WHERE (
`Projects`.`name` = 'Some Project'
AND NOT (
`Projects`.`id` IN (1,2,3)
AND
`Projects`.`description` LIKE 'Hello%'
)
)

使用函数(不仅仅是列)的高级查询

¥Advanced queries with functions (not just columns)

如果你想获得像 WHERE char_length("content") = 7 这样的东西怎么办?

¥What if you wanted to obtain something like WHERE char_length("content") = 7?

Post.findAll({
where: sequelize.where(sequelize.fn('char_length', sequelize.col('content')), 7),
});
// SELECT ... FROM "posts" AS "post" WHERE char_length("content") = 7

请注意 sequelize.fnsequelize.col 方法的用法,它们分别用于指定 SQL 函数调用和表列。应该使用这些方法而不是传递纯字符串(例如 char_length(content)),因为 Sequelize 需要以不同的方式处理这种情况(例如,使用其他符号转义方法)。

¥Note the usage of the sequelize.fn and sequelize.col methods, which should be used to specify an SQL function call and a table column, respectively. These methods should be used instead of passing a plain string (such as char_length(content)) because Sequelize needs to treat this situation differently (for example, using other symbol escaping approaches).

如果你需要更复杂的东西怎么办?

¥What if you need something even more complex?

Post.findAll({
where: {
[Op.or]: [
sequelize.where(sequelize.fn('char_length', sequelize.col('content')), 7),
{
content: {
[Op.like]: 'Hello%',
},
},
{
[Op.and]: [
{ status: 'draft' },
sequelize.where(sequelize.fn('char_length', sequelize.col('content')), {
[Op.gt]: 10,
}),
],
},
],
},
});

上面生成了以下 SQL:

¥The above generates the following SQL:

SELECT
...
FROM "posts" AS "post"
WHERE (
char_length("content") = 7
OR
"post"."content" LIKE 'Hello%'
OR (
"post"."status" = 'draft'
AND
char_length("content") > 10
)
)

仅限 Postgres 的范围运算符

¥Postgres-only Range Operators

可以使用所有支持的运算符来查询范围类型。

¥Range types can be queried with all supported operators.

请记住,提供的范围值也可以是 定义绑定包含/排除

¥Keep in mind, the provided range value can define the bound inclusion/exclusion as well.

[Op.contains]: 2,            // @> '2'::integer  (PG range contains element operator)
[Op.contains]: [1, 2], // @> [1, 2) (PG range contains range operator)
[Op.contained]: [1, 2], // <@ [1, 2) (PG range is contained by operator)
[Op.overlap]: [1, 2], // && [1, 2) (PG range overlap (have points in common) operator)
[Op.adjacent]: [1, 2], // -|- [1, 2) (PG range is adjacent to operator)
[Op.strictLeft]: [1, 2], // << [1, 2) (PG range strictly left of operator)
[Op.strictRight]: [1, 2], // >> [1, 2) (PG range strictly right of operator)
[Op.noExtendRight]: [1, 2], // &< [1, 2) (PG range does not extend to the right of operator)
[Op.noExtendLeft]: [1, 2], // &> [1, 2) (PG range does not extend to the left of operator)

已弃用:运算符别名

¥Deprecated: Operator Aliases

在 Sequelize v4 中,可以指定字符串来引用运算符,而不是使用符号。现在已弃用并强烈建议不要这样做,并且可能会在下一个主要版本中将其删除。如果确实需要,可以在 Sequelize 构造函数中传递 operatorAliases 选项。

¥In Sequelize v4, it was possible to specify strings to refer to operators, instead of using Symbols. This is now deprecated and heavily discouraged, and will probably be removed in the next major version. If you really need it, you can pass the operatorAliases option in the Sequelize constructor.

例如:

¥For example:

const { Sequelize, Op } = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:', {
operatorsAliases: {
$gt: Op.gt,
},
});

// Now we can use `$gt` instead of `[Op.gt]` in where clauses:
Foo.findAll({
where: {
$gt: 6, // Works like using [Op.gt]
},
});

简单的 UPDATE 查询

¥Simple UPDATE queries

更新查询也接受 where 选项,就像上面显示的读取查询一样。

¥Update queries also accept the where option, just like the read queries shown above.

// Change everyone without a last name to "Doe"
await User.update(
{ lastName: 'Doe' },
{
where: {
lastName: null,
},
},
);

简单的 DELETE 查询

¥Simple DELETE queries

删除查询也接受 where 选项,就像上面显示的读取查询一样。

¥Delete queries also accept the where option, just like the read queries shown above.

// Delete everyone named "Jane"
await User.destroy({
where: {
firstName: 'Jane',
},
});

要销毁所有内容,可以使用 TRUNCATE SQL:

¥To destroy everything the TRUNCATE SQL can be used:

// Truncate the table
await User.destroy({
truncate: true,
});

批量创建

¥Creating in bulk

Sequelize 提供了 Model.bulkCreate 方法,允许一次创建多条记录,只需一次查询。

¥Sequelize provides the Model.bulkCreate method to allow creating multiple records at once, with only one query.

Model.bulkCreate 的用法与 Model.create 非常相似,都是接收对象数组而不是单个对象。

¥The usage of Model.bulkCreate is very similar to Model.create, by receiving an array of objects instead of a single object.

const captains = await Captain.bulkCreate([{ name: 'Jack Sparrow' }, { name: 'Davy Jones' }]);
console.log(captains.length); // 2
console.log(captains[0] instanceof Captain); // true
console.log(captains[0].name); // 'Jack Sparrow'
console.log(captains[0].id); // 1 // (or another auto-generated value)

但是,默认情况下,bulkCreate 不会对要创建的每个对象运行验证(create 会这样做)。要使 bulkCreate 也运行这些验证,你必须传递 validate: true 选项。这会降低性能。使用示例:

¥However, by default, bulkCreate does not run validations on each object that is going to be created (which create does). To make bulkCreate run these validations as well, you must pass the validate: true option. This will decrease performance. Usage example:

const Foo = sequelize.define('foo', {
name: {
type: DataTypes.TEXT,
validate: {
len: [4, 6],
},
},
});

// This will not throw an error, both instances will be created
await Foo.bulkCreate([{ name: 'abc123' }, { name: 'name too long' }]);

// This will throw an error, nothing will be created
await Foo.bulkCreate([{ name: 'abc123' }, { name: 'name too long' }], {
validate: true,
});

如果你直接接受来自用户的值,则限制你要实际插入的列可能会有所帮助。为了支持这一点,bulkCreate() 接受 fields 选项,这是一个定义必须考虑哪些字段的数组(其余字段将被忽略)。

¥If you are accepting values directly from the user, it might be beneficial to limit the columns that you want to actually insert. To support this, bulkCreate() accepts a fields option, an array defining which fields must be considered (the rest will be ignored).

await User.bulkCreate([{ username: 'foo' }, { username: 'bar', admin: true }], {
fields: ['username'],
});
// Neither foo nor bar are admins.

排序和分组

¥Ordering and Grouping

Sequelize 提供 ordergroup 选项来与 ORDER BYGROUP BY 配合使用。

¥Sequelize provides the order and group options to work with ORDER BY and GROUP BY.

排序

¥Ordering

order 选项采用一个项目数组来对查询进行排序或使用 sequelize 方法。这些项目本身就是 [column, direction] 形式的数组。该列将被正确转义,并且将在有效方向白名单中检查方向(例如 ASCDESCNULLS FIRST 等)。

¥The order option takes an array of items to order the query by or a sequelize method. These items are themselves arrays in the form [column, direction]. The column will be escaped correctly and the direction will be checked in a whitelist of valid directions (such as ASC, DESC, NULLS FIRST, etc).

Subtask.findAll({
order: [
// Will escape title and validate DESC against a list of valid direction parameters
['title', 'DESC'],

// Will order by max(age)
sequelize.fn('max', sequelize.col('age')),

// Will order by max(age) DESC
[sequelize.fn('max', sequelize.col('age')), 'DESC'],

// Will order by otherfunction(`col1`, 12, 'lalala') DESC
[sequelize.fn('otherfunction', sequelize.col('col1'), 12, 'lalala'), 'DESC'],

// Will order an associated model's createdAt using the model name as the association's name.
[Task, 'createdAt', 'DESC'],

// Will order through an associated model's createdAt using the model names as the associations' names.
[Task, Project, 'createdAt', 'DESC'],

// Will order by an associated model's createdAt using the name of the association.
['Task', 'createdAt', 'DESC'],

// Will order by a nested associated model's createdAt using the names of the associations.
['Task', 'Project', 'createdAt', 'DESC'],

// Will order by an associated model's createdAt using an association object. (preferred method)
[Subtask.associations.Task, 'createdAt', 'DESC'],

// Will order by a nested associated model's createdAt using association objects. (preferred method)
[Subtask.associations.Task, Task.associations.Project, 'createdAt', 'DESC'],

// Will order by an associated model's createdAt using a simple association object.
[{ model: Task, as: 'Task' }, 'createdAt', 'DESC'],

// Will order by a nested associated model's createdAt simple association objects.
[{ model: Task, as: 'Task' }, { model: Project, as: 'Project' }, 'createdAt', 'DESC'],
],

// Will order by max age descending
order: sequelize.literal('max(age) DESC'),

// Will order by max age ascending assuming ascending is the default order when direction is omitted
order: sequelize.fn('max', sequelize.col('age')),

// Will order by age ascending assuming ascending is the default order when direction is omitted
order: sequelize.col('age'),

// Will order randomly based on the dialect (instead of fn('RAND') or fn('RANDOM'))
order: sequelize.random(),
});

Foo.findOne({
order: [
// will return `name`
['name'],
// will return `username` DESC
['username', 'DESC'],
// will return max(`age`)
sequelize.fn('max', sequelize.col('age')),
// will return max(`age`) DESC
[sequelize.fn('max', sequelize.col('age')), 'DESC'],
// will return otherfunction(`col1`, 12, 'lalala') DESC
[sequelize.fn('otherfunction', sequelize.col('col1'), 12, 'lalala'), 'DESC'],
// will return otherfunction(awesomefunction(`col`)) DESC, This nesting is potentially infinite!
[sequelize.fn('otherfunction', sequelize.fn('awesomefunction', sequelize.col('col'))), 'DESC'],
],
});

回顾一下,订单数组的元素可以如下:

¥To recap, the elements of the order array can be the following:

  • 一个字符串(将自动加引号)

    ¥A string (which will be automatically quoted)

  • 一个数组,其第一个元素将被引用,第二个元素将逐字附加

    ¥An array, whose first element will be quoted, second will be appended verbatim

  • 具有 raw 字段的对象:

    ¥An object with a raw field:

    • raw 的内容将逐字添加,不加引号

      ¥The content of raw will be added verbatim without quoting

    • 其他一切都被忽略,如果未设置 raw,查询将失败

      ¥Everything else is ignored, and if raw is not set, the query will fail

  • Sequelize.fn 的调用(将在 SQL 中生成函数调用)

    ¥A call to Sequelize.fn (which will generate a function call in SQL)

  • 调用 Sequelize.col(将引用列名称)

    ¥A call to Sequelize.col (which will quote the column name)

分组

¥Grouping

分组和排序的语法相同,只是分组不接受方向作为数组的最后一个参数(没有 ASCDESCNULLS FIRST 等)。

¥The syntax for grouping and ordering are equal, except that grouping does not accept a direction as last argument of the array (there is no ASC, DESC, NULLS FIRST, etc).

你还可以将字符串直接传递给 group,该字符串将直接(逐字)包含到生成的 SQL 中。请谨慎使用,不要与用户生成的内容一起使用。

¥You can also pass a string directly to group, which will be included directly (verbatim) into the generated SQL. Use with caution and don't use with user generated content.

Project.findAll({ group: 'name' });
// yields 'GROUP BY name'

限制和分页

¥Limits and Pagination

limitoffset 选项允许你使用限制/分页:

¥The limit and offset options allow you to work with limiting / pagination:

// Fetch 10 instances/rows
Project.findAll({ limit: 10 });

// Skip 8 instances/rows
Project.findAll({ offset: 8 });

// Skip 5 instances and fetch the 5 after that
Project.findAll({ offset: 5, limit: 5 });

通常这些与 order 选项一起使用。

¥Usually these are used alongside the order option.

工具方法

¥Utility methods

Sequelize 还提供了一些实用方法。

¥Sequelize also provides a few utility methods.

count

count 方法只是计算数据库中元素的出现次数。

¥The count method simply counts the occurrences of elements in the database.

console.log(`There are ${await Project.count()} projects`);

const amount = await Project.count({
where: {
id: {
[Op.gt]: 25,
},
},
});
console.log(`There are ${amount} projects with an id greater than 25`);

maxminsum

¥max, min and sum

Sequelize 还提供了 maxminsum 便捷方法。

¥Sequelize also provides the max, min and sum convenience methods.

假设我们有 3 个用户,年龄分别为 10、5 和 40。

¥Let's assume we have three users, whose ages are 10, 5, and 40.

await User.max('age'); // 40
await User.max('age', { where: { age: { [Op.lt]: 20 } } }); // 10
await User.min('age'); // 5
await User.min('age', { where: { age: { [Op.gt]: 5 } } }); // 10
await User.sum('age'); // 55
await User.sum('age', { where: { age: { [Op.gt]: 5 } } }); // 50

incrementdecrement

Sequelize 还提供了 increment 便捷方法。

¥Sequelize also provides the increment convenience method.

假设我们有一个用户,年龄为 10 岁。

¥Let's assume we have a user, whose age is 10.

await User.increment({ age: 5 }, { where: { id: 1 } }); // Will increase age to 15
await User.increment({ age: -5 }, { where: { id: 1 } }); // Will decrease age to 5