【问题标题】:Sequelize - Adding a limit to a query with an include, fails to properly limit retrievalsSequelize - 使用包含向查询添加限制,无法正确限制检索
【发布时间】:2020-08-28 21:47:36
【问题描述】:

问题描述

使用 SubQuery 向 Sequelize Query 添加限制无法限制检索。引用此错误的多个在线资源并且没有解决方案。这是 Sequelize 错误还是用户错误?

你在做什么?

ThreadFolderUser.findAll({
order: [
  ['updated_at', 'DESC']
],
where: {
  user_id,
  folder_id,
  deleted,
  archived,
},
distinct: true,
offset,
limit: 10,
include: [
  {
    model: Thread,
    include: [
      { model: Email, include: [Attachment] },
    ]
  }
],

})


协会

// ThreadFolderUser (assoc table) - Thread / Folder / User (tables)
User.hasMany(ThreadFolderUser, { foreignKey: 'user_id' })
ThreadFolderUser.belongsTo(User, { foreignKey: 'user_id' })
Folder.hasMany(ThreadFolderUser, { foreignKey: 'folder_id' })
ThreadFolderUser.belongsTo(Folder, { foreignKey: 'folder_id' })
Thread.hasMany(ThreadFolderUser, { foreignKey: 'thread_id' })
ThreadFolderUser.belongsTo(Thread, { foreignKey: 'thread_id' })

// Thread - Emails
Thread.hasMany(Email, { foreignKey: 'thread_id' })
Email.belongsTo(Thread, { foreignKey: 'thread_id' })

// Email - Attachments
Email.hasMany(Attachment, { foreignKey: 'email_id' })
Attachment.belongsTo(Email, { foreignKey: 'email_id' })

您预计会发生什么?

我预计从 AssociationTable 中检索到 10 条记录(基于当前设置为 10 条的限制),因为我在数据库中至少有 15 条记录与此查询匹配。

实际发生了什么?

在我的情况下返回 6,而不是 10(限制设置为 10)。而不是拉前 10 场比赛。


附加上下文

如果我删除 限制,它会按预期工作(即使包含包含)。

如果我删除 include,它会按预期工作(即使有限制)。

如果我复制/粘贴 Sequelize 生成的 SQL 查询并将其直接插入 Workbench,它会检索适当数量的行。

问题似乎是 limit 结合 include 导致查询仅检索在数据库中搜索的前 10 个匹配的记录。


对同一问题的其他参考,但未提供适当的解决方案:


环境

  • 续集版本:v5.21.3
  • Node.js 版本:v12.13.1
  • 操作系统:AWS Lambda 函数
  • TypeScript 版本:3.7.2

我很清楚,这个完全相同的问题已在多个其他线程和平台中提出——正如我在上面链接了其中的一些——但是它们都没有直接的答案,其中 1 个被标记一个无关紧要的点作为没有解决预期问题的答案。我希望我们能得到这个问题的答案,或者除了硬编码 SQL 查询(最后的手段)之外的现实解决方法。

如果 Sequelize 无法在同一个查询中处理包含包含的限制,那将是不可想象的,因此我这边一定有一些缺失/用户错误。我已经搜索了多次,并且肯定是从 Sequelize 文档开始的,其中没有引用这个问题或类似的例子,或者结合限制和包含可能出现的任何问题。

非常感谢您为帮助解决此问题所做的任何贡献。希望有一些@Sequelize 工程师能够帮助回答这个问题:)

【问题讨论】:

  • 显示关联定义
  • @Anatoly - 谢谢,添加了他们。
  • AssociationModel 的关联在哪里?
  • @Anatoly 已更新 - 我试图使用通用名称来保持简单,但在添加需要更新的关联时忘记了。

标签: mysql node.js sequelize.js serverless-framework


【解决方案1】:

在您进行 sequelize 以通过单独的查询获取包含的关联对象之前,您无法正确限制包含 hasMany 关联的 sequelize 查询。在您的查询中,您有 include 与关联 Thread.hasMany(Email 因此您应该在电子邮件中指出 separate: true 像这样(这个也适用于附件关联):

include: [
      { 
       model: Email, 
       separate: true,
       include: [{
         model: Attachment,
         separate: true
       }] },
    ]

另外,您不需要指出 distinct: true,因为我们已经指出将 hasMany 关联分离到自己的查询中。

hasMany includes(尤其是嵌套的 hasMany)的另一个问题 它们在 SQL 查询中变成 JOIN,这意味着数据库将一定数量的主记录乘以一定数量的嵌套记录,依此类推。 例如:100 条主记录,每条有 100 条链接记录,每条都有自己链接的 100 条记录(主题 -> 电子邮件 -> 附件)。总而言之,您创建一个数据库来一次查询 100*100*100 - 100 万条记录!它通常会导致内存不足

关于 LIMIT 和 hasMany:数据库选择 100 条主记录,每个记录有 100 条链接(一次 10000 条记录),然后从这 10000 条记录中获取前 10 条记录(而不是来自 100 条主记录)。这就是 SQL 查询的工作原理!

【讨论】:

  • 这是一个了不起的答案,我还没有看到任何与这样的逻辑答案相近的东西。感谢您提供解决方案,最重要的是解释了为什么它会以这种方式工作。我正在处理一个大型数据库,来自旧数据库的超过 1000 万条电子邮件记录(1 个表中的单个记录),我将创建一个脚本将它们转换为连接到相关电子邮件和附件的线程。由于这个原因,我希望 JOIN 语句不会接近 100*100*100,但要点很好,我想我理解了,我会密切关注这一点。
  • 为了确认是否理解这一点,如果我拉 20 个线程,平均每个 15 封电子邮件和每个平均 5 个附件,那将是 1500 的查询?还是它实际上会拉(所有匹配的线程)*(这些线程上的所有匹配的电子邮件)*(此电子邮件上的所有匹配的附件),然后限制到主要的 20 个,所以我的查询可能总是会拉更多的记录在过滤到主要的 20 限制/偏移之前 - 我的这种想法是否正确?
  • 所以我关心的是主查询的 where 语句,以确保它只提取必要的关联记录,在这种情况下,关联记录特定于 1)用户和 2)邮箱(其中我'已命名文件夹),因此如果有 500 条记录与此用户/文件夹组合匹配,它将始终从 500 * 匹配电子邮件到 500 * 匹配电子邮件附件开始 - 然后配对到限制和偏移量,对吗?
  • 如果您指定separate: true,那么您将获得 20 个线程(1 个查询,其中 20 个线程仅被过滤和限制),然后是 20 个单独的查询以获取电子邮件,然后是 20*5 个单独的查询以获取附件。
  • 仅针对已过滤和受限的线程查询电子邮件和附件
猜你喜欢
  • 1970-01-01
  • 2020-12-02
  • 2015-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-11
相关资源
最近更新 更多