【问题标题】:cannot chain joins using string to left_joins不能将使用字符串的连接链接到 left_joins
【发布时间】:2026-01-22 00:50:01
【问题描述】:

总结:

我通过警报在附件和规则之间建立了多对多的关系。 我有给定的规则和给定的附件选择(具有给定 bug_id 的附件)。 我需要浏览所有选定的附件,并使用不同的 CSS 背景颜色指示是否存在规则警报。

外连接

我通过以下查询得到了正确的结果:

SELECT attachments.*, alerts.rule_id
FROM attachments
     LEFT OUTER JOIN alerts ON alerts.attachment_id = attachments.id
                               and alerts.rule_id = 9
WHERE attachments.bug_id;

我正在寻找类似的东西:

bug.attachments
   .left_joins(alerts: {'rules.id' => 9})
   .select('attachments.*, alerts.rule_id')

数据库

class Alert < ApplicationRecord
  belongs_to :attachment

class Attachment < ApplicationRecord
  has_many :alerts

attachments
|  id   | bug_id |
| 14612 | 38871  |
| 14613 | 38871  |
| 14614 | 38871  |

alerts
| attachment_id | rule_id |
|     14612     |    9    |
|     14614     |    8    |

From 子句中的条件

如果 FROM 子句中没有 alerts.rule_id = 9 条件,我们会得到以下结果:

|  id   | rule_id |
| 14612 |    9    |
| 14614 |    8    |
| 14613 |   NULL  |

所以有一个 WHERE 子句 WHERE alerts.rule_id = 9 or alerts.rule_id is NULL 会丢失 14612 的结果

因此以下内容将不起作用:

bug.attachments
   .joins(:alerts)
   .select('attachments.*, alerts.rule_id')
   .where( ??? )

编辑

以上是我原始问题的简化和更正版本。 原问题如下:

alerts属于规则和附件,附件属于bug。

class Alert < ApplicationRecord
  belongs_to :attachment
  belongs_to :rule

class Attachment < ApplicationRecord
  belongs_to :bug
  has_many :alerts

class Bug < ApplicationRecord
  has_many :attachments

对于给定的规则,我需要显示给定错误的所有附件,以及是否有警报。我想要以下 SQL:

SELECT attachments.*, alerts.id as alert_id
FROM `attachments`
  LEFT OUTER JOIN `alerts` ON `alerts`.`attachment_id` = `attachments`.`id`
  LEFT OUTER JOIN `rules` ON `rules`.`id` = `alerts`.`rule_id` AND rules.id = 9
WHERE `attachments`.`bug_id` = 38871

我可以从:

bug.attachments
   .joins("LEFT OUTER JOIN `alerts` ON `alerts`.`attachment_id` = `attachments`.`id`")
   .joins("LEFT OUTER JOIN `rules` ON `rules`.`id` = `alerts`.`rule_id` AND rules.id = 9")
   .select('attachments.*, alerts.id as alert_id')
   .map{|attach| [attach.file_name, attach.alert_id]}

我想知道的是如何避免使用字符串 SQL 片段调用连接。

我正在寻找类似的东西:

bug.attachments
   .left_joins(alerts: {rule: {'rules.id' => 9}})
   .select('attachments.*, alerts.id as alert_id')
   .map{|attach| [attach.file_name, attach.alert_id]}

有没有办法避免传递 SQL 字符串?

【问题讨论】:

  • 注意:rules.id = 9 需要在join子句中,而不是where子句中,否则我将无法得到正确的结果。

标签: ruby-on-rails activerecord left-join ruby-on-rails-5


【解决方案1】:

实际上,我认为您可以通过将 rules.id = 9 放在 where 子句中来获得正确的结果。

SELECT attachments.*, alerts.id as alert_id
FROM `attachments`
LEFT OUTER JOIN `alerts` ON `alerts`.`attachment_id` = `attachments`.`id`
LEFT OUTER JOIN `rules` ON `rules`.`id` = `alerts`.`rule_id`
WHERE `attachments`.`bug_id` = 38871 AND (rules.id = 9 OR rules.id IS NULL)

【讨论】:

  • 不。我在mysql中做到了,我得到了不同的结果。当它在 where 子句中时,我只得到 rules.id = 9 的记录。我在 from 子句中需要它,所以当 rules.id 为 null 时我得到外连接结果。
  • 这很奇怪。我确信它会起作用。我必须进行一些检查:D。
  • 好的,我知道现在有什么不同了。您的查询会生成包含所有规则的所有警报的附件,但仅连接那些 rule_id = 9 的数据。你仍然得到属于其他规则的其他警报,只是没有加入数据。我的查询结果是带有仅 rule_id = 9 或 NULL 警报的附件。你需要哪一个?
  • 我没有看到 or 为空。有机会我会试试的。
  • 您可以使用SELECT attachments.*, alerts.id as alert_id FROM attachments LEFT OUTER JOIN alerts ON alerts.attachment_id = attachments.id LEFT OUTER JOIN rules ON rules.id = alerts.rule_id AND rules.id = 9 WHERE attachments.bug_id = 38871 AND attachments.id NOT IN (SELECT at.id FROM attachments at LEFT OUTER JOIN alerts al ON al.attachment_id = at.id LEFT OUTER JOIN r ON r.id = al.rule_id WHERE at.bug_id = 38871 AND (r.id = 9 OR r.id IS NULL)) 来查看两个查询之间的差异。
最近更新 更多