如何在rails中使用多个连接进行查询?

时间:2014-04-29 15:34:00

标签: ruby-on-rails ruby activerecord

我有3个模型 - DatapointsDimensions以及将这2个模型绑定在一起的模型 - DatapointDimensions

class Datapoint < ActiveRecord::Base
  belongs_to :metric
  has_many :datapoint_dimensions
  has_many :dimensions, :through => :datapoint_dimensions

  validates :metric, :presence => true
  validates :value, :presence => true
  validates :timestamp, :presence => true
  validates :duration, :presence => true
end

class Dimension < ActiveRecord::Base
  belongs_to :dimension_group
  belongs_to :parent_dimension, class_name: "Dimension", foreign_key: "parent_dimension_id"
  has_many :child_dimensions, class_name: "Dimension", foreign_key: "parent_dimension_id"
  has_many :datapoint_dimensions
  has_many :datapoints, through: :datapoint_dimensions

  validates :dimension_group, :presence => true
  validates :code, :presence => true, :uniqueness => true
  validates :name, :presence => true, :uniqueness => true
end

class DatapointDimension < ActiveRecord::Base
  belongs_to :datapoint
  belongs_to :dimension

  validates :datapoint, :presence => true
  validates :dimension, :presence => true
end

现在,我需要编写一个查询,其中我检查具有特定度量,特定持续时间和DatapointDimension的Datapoint,它具有3个维度的组合

表格数据是这样的

----datapoints----
id      metric_id       duration
1          13            1M2012
2          13            1M2012

----datapoint_dimensions----
datapoint_id              dimension_id
   1                           2
   1                           45
   1                          127
   2                           2
   2                           38

我希望能够在datapoint_dimensions

中搜索metric_id = 13的Datapoints与尺寸2,55和127(全部3个)相关联

我试过

Datapoint.where(metric_id: 13).where(duration: '1M2012').joins(:datapoint_dimensions).where(datapoint_dimensions: {dimension_id: 2, dimension_id: 45, dimension_id: 127}).readonly(false)

这将返回两个数据点(id = 1和id = 2) 但我希望它只返回id = 1

的数据点

提前致谢。

1 个答案:

答案 0 :(得分:0)

您看到的问题与包含where参数的dimension_id子句有关。在您的情况下,您传递的哈希值包含三个键值对,但键的名称实际上是相同的。结果,当你认为你正在传递

{dimension_id: 2, dimension_id: 45, dimension_id: 127}

您实际上正在传递

{dimension_id: 2}

因为您基本上覆盖(或挤压)同名的其他键。 注意:&#34;挤压&#34;在其他版本的Ruby中,行为可能会有所不同。在我的(Ruby 2.0+)上,我的参数是127,而你指的是2.

由于您说要提取包含所有这三个Datapoint值的dimension_id对象,因此可以通过使用WHERE ... IN和子查询来实现此目的。过滤不完全匹配的结果。它比原始版本复杂一点,但试试这个(格式化为可读性):

Datapoint.joins(:datapoint_dimensions)
         .where(metric_id: 13, duration: '1M2012')
         .where(datapoint_dimensions: {dimension_id: [2, 45, 127]})
         .where("datapoint_id != (#{                 

            DatapointDimension
              .select(:datapoint_id)
              .where("dimension_id NOT IN (?)", [2, 45, 127]).to_sql

         })").group(:datapoint_id)

注意子查询和笨拙的字符串插值。可能有更好的方法来写这个,但这是我想到的唯一方法。

Rails 4具有where.not函数,这使得该子查询稍微不那么笨拙,但实质上此查询将返回dimension_id为2,45和127的每一行。

另请注意,如果您愿意,可以将.where条款压缩到一个方法调用中,我只是将其分开以便于阅读。

如果这有所不同,请告诉我。

修改

如果上述方法无效,请尝试以下方法:

Datapoint.joins(:datapoint_dimensions)
         .where(metric_id: 13, duration: '1M2012')
         .where(datapoint_dimensions: {dimension_id: [2, 45, 127]})
         .where("NOT EXISTS (SELECT 1 FROM datapoint_dimensions dd2 WHERE dd2.datapoint_id = datapoint_dimensions.datapoint_id AND dimension_id NOT IN (2, 45, 127))")
         .group(:datapoint_id)