如何将含行值比较的SQL查询转换为Sequelize(v6)查询?
问题
我有如下SQL查询:
SELECT * FROM funds WHERE (fund_id, created_at) > ($1, $2) ORDER BY created_at DESC LIMIT 5;
我正尝试将其转换为Sequelize(v6)中findAll(...)查询的过滤对象,目标是通过(fund_id, created_at)实现键集分页,数据库用的是SQLite,且已为(fund_id, created_at)创建索引。
目前我的实现如下:
{ where: { [Op.and]: [ { fundId: { [Op.gt]: prev_fund_id } }, { createdAt: { [Op.gt]: prev_created_at } }, ], }, limit: 5, order: [["created_at", "DESC"]], }
但生成的SQL和预期不符,日志输出的SQL是:
SELECT `fund_id`, `name`, `description`, `created_at`, `updated_at` FROM `funds` WHERE (`fund_id` > '04e5b202-e62e-417a-b9d8-8cd5b6a1cb8f' AND `created_at` > '2023-02-13 07:38:36.683 +00:00') ORDER BY `created_at` DESC LIMIT 5;
我知道行值比较和AND逻辑并不等价,但Sequelize似乎没有对应的Op运算符,请问该如何用Sequelize实现这个查询?
解决方案
方法1:用Sequelize.literal写入原生行值比较条件
这是最稳妥的实现方式,直接通过原生SQL片段来实现行值比较,还能避免SQL注入:
const { Op, literal } = require('sequelize'); const queryOptions = { where: literal('(fund_id, created_at) > (:prevFundId, :prevCreatedAt)'), replacements: { prevFundId: prev_fund_id, prevCreatedAt: prev_created_at }, limit: 5, order: [["created_at", "DESC"]], };
方法2:利用Sequelize的数组比较语法(需兼容验证)
Sequelize v6对部分数据库支持行值比较的简化写法,可以通过字段数组与值数组的比较实现:
const { Op } = require('sequelize'); const queryOptions = { where: { [['fundId', 'createdAt']]: { [Op.gt]: [prev_fund_id, prev_created_at] } }, limit: 5, order: [["created_at", "DESC"]], };
注:这种写法需要确认你的Sequelize版本和SQLite驱动是否兼容,若出现语法报错,优先使用第一种literal方案。
关键说明
你之前的AND逻辑确实和行值比较不等价:行值比较(a,b) > (x,y)的实际逻辑是(a > x) OR (a = x AND b > y),这才是键集分页需要的规则——要么fund_id更大,要么fund_id相同时created_at更大,这样才能正确获取下一页的连续数据。
内容的提问来源于stack exchange,提问作者DaleCS
相关产品推荐
相关产品推荐

