Rails/ActiveRecord:通过复合主键预先加载连接 table 条目
Rails/ActiveRecord: Eager loading join table entries via a composite primary key
我在 Rails (4.2.4) (w/MySQL) 中使用 ActiveRecord 急切加载连接 table 时遇到问题。问题是 join table 使用了复合主键,这导致了一些奇怪的事情?急切加载的行为。这里有一个例子来说明:
class Student < ActiveRecord::Base
has_many :course_student_joins
has_many :courses, through: :course_student_joins
end
class Course < ActiveRecord::Base
has_many :course_student_joins
has_many :students, through: :course_student_joins
end
class CourseStudentJoin < ActiveRecord::Base
belongs_to :course
belongs_to :student
end
Table: courses
id name
1 Course 1
2 Course 2
Table: students
id name
1 Joey
2 Johnny
3 Dee Dee
Table: course_student_joins
course_id student_id extra_id
1 1 5
2 2 6
所以course_student_joins是经典的多对多连接table。这是有趣的一点,如果 course_student_joins table 上的 PRIMARY KEY 仅由 course_id(单个字段)组成,这有效:
> Course.eager_load(:course_student_joins).first.course_student_joins
Result:
SQL (0.2ms) SELECT `courses`.`id` AS t0_r0, `courses`.`name` AS t0_r1, `course_student_joins`.`course_id` AS t1_r0, `course_student_joins`.`student_id` AS t1_r1, `course_student_joins`.`extra_id` AS t1_r2 FROM `courses` LEFT OUTER JOIN `course_student_joins` ON `course_student_joins`.`course_id` = `courses`.`id` WHERE `courses`.`id` = 1 AND `courses`.`id` IN (1)
=> #<ActiveRecord::Associations::CollectionProxy [#<CourseStudentJoin course_id: 1, student_id: 1, extra_id: 5>]>
如果PRIMARY KEY被(数据库端)更改为(course_id,student_id)的复合键,这就是我们想要的(因为一个课程不能有相同的学生两次并且主键需要是唯一的),这就是我们得到的:
> Course.eager_load(:course_student_joins).find_by(id:1).course_student_joins
Result:
SQL (0.2ms) SELECT `courses`.`id` AS t0_r0, `courses`.`name` AS t0_r1, `course_student_joins`.`course_id` AS t1_r0, `course_student_joins`.`student_id` AS t1_r1, `course_student_joins`.`extra_id` AS t1_r2 FROM `courses` LEFT OUTER JOIN `course_student_joins` ON `course_student_joins`.`course_id` = `courses`.`id` WHERE `courses`.`id` = 1 AND `courses`.`id` IN (1)
=> #<ActiveRecord::Associations::CollectionProxy []>
注意第二个结果中的空集合,SQL 在两个结果中保持完全相同(预期)。
我们在 has_many 声明中使用了 foreign_key、primary_key 设置,甚至 'solution' 改变了加载以反映它是如何执行的rails 2.0 及以下版本。也玩过 includes/references 。运气不好,any/all 帮助表示感谢。
最终目标是提出一个 ActiveRecord 课程集合,其中包含急切加入的学生和连接中的条目 table,因为我们需要对连接中的某些值进行操作 table 在一个 ActiveModel::Serializer 中,我们将通过它传递集合。
最 Rails 友好的方法是自动递增 id
整数主键 + (course_id, student_id)
上的唯一索引。
您是否有任何理由想要加载和 CourseStudentJoin
记录?无论如何,您似乎不想直接操作它们,最好还是做
这样的事情
Course.includes(:students).where(active: true, category: "Math").foo.bar
Student.includes(:courses).where(state: "CA").foo.bar
感觉您不需要主键来进行联接 table。您可以只在迁移文件上指定 id: false
。
还要注意更改数据库参数 () 而不告诉 rails schema 这些更改,您可以通过 dry 运行 rake db:migrate
更新 schema
你最后要找的是 HABTM 关系。 has_and_belongs_to_many: http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_and_belongs_to_many
您只需要:
student = Student.find(1)
student.courses
跟进。将一个单一的、自动递增的列作为永远不会被使用的主键,不太合适。 composite_primary_keys gem 确实影响了 ActiveRecord 中的 eager_load 机制并解决了这个问题。我们没有考虑它,因为我们不需要 gem 的主要功能,但它确实以最可接受的方式解决了这个问题。
我在 Rails (4.2.4) (w/MySQL) 中使用 ActiveRecord 急切加载连接 table 时遇到问题。问题是 join table 使用了复合主键,这导致了一些奇怪的事情?急切加载的行为。这里有一个例子来说明:
class Student < ActiveRecord::Base
has_many :course_student_joins
has_many :courses, through: :course_student_joins
end
class Course < ActiveRecord::Base
has_many :course_student_joins
has_many :students, through: :course_student_joins
end
class CourseStudentJoin < ActiveRecord::Base
belongs_to :course
belongs_to :student
end
Table: courses
id name
1 Course 1
2 Course 2
Table: students
id name
1 Joey
2 Johnny
3 Dee Dee
Table: course_student_joins
course_id student_id extra_id
1 1 5
2 2 6
所以course_student_joins是经典的多对多连接table。这是有趣的一点,如果 course_student_joins table 上的 PRIMARY KEY 仅由 course_id(单个字段)组成,这有效:
> Course.eager_load(:course_student_joins).first.course_student_joins
Result:
SQL (0.2ms) SELECT `courses`.`id` AS t0_r0, `courses`.`name` AS t0_r1, `course_student_joins`.`course_id` AS t1_r0, `course_student_joins`.`student_id` AS t1_r1, `course_student_joins`.`extra_id` AS t1_r2 FROM `courses` LEFT OUTER JOIN `course_student_joins` ON `course_student_joins`.`course_id` = `courses`.`id` WHERE `courses`.`id` = 1 AND `courses`.`id` IN (1)
=> #<ActiveRecord::Associations::CollectionProxy [#<CourseStudentJoin course_id: 1, student_id: 1, extra_id: 5>]>
如果PRIMARY KEY被(数据库端)更改为(course_id,student_id)的复合键,这就是我们想要的(因为一个课程不能有相同的学生两次并且主键需要是唯一的),这就是我们得到的:
> Course.eager_load(:course_student_joins).find_by(id:1).course_student_joins
Result:
SQL (0.2ms) SELECT `courses`.`id` AS t0_r0, `courses`.`name` AS t0_r1, `course_student_joins`.`course_id` AS t1_r0, `course_student_joins`.`student_id` AS t1_r1, `course_student_joins`.`extra_id` AS t1_r2 FROM `courses` LEFT OUTER JOIN `course_student_joins` ON `course_student_joins`.`course_id` = `courses`.`id` WHERE `courses`.`id` = 1 AND `courses`.`id` IN (1)
=> #<ActiveRecord::Associations::CollectionProxy []>
注意第二个结果中的空集合,SQL 在两个结果中保持完全相同(预期)。
我们在 has_many 声明中使用了 foreign_key、primary_key 设置,甚至 'solution' 改变了加载以反映它是如何执行的rails 2.0 及以下版本。也玩过 includes/references 。运气不好,any/all 帮助表示感谢。
最终目标是提出一个 ActiveRecord 课程集合,其中包含急切加入的学生和连接中的条目 table,因为我们需要对连接中的某些值进行操作 table 在一个 ActiveModel::Serializer 中,我们将通过它传递集合。
最 Rails 友好的方法是自动递增 id
整数主键 + (course_id, student_id)
上的唯一索引。
您是否有任何理由想要加载和 CourseStudentJoin
记录?无论如何,您似乎不想直接操作它们,最好还是做
Course.includes(:students).where(active: true, category: "Math").foo.bar
Student.includes(:courses).where(state: "CA").foo.bar
感觉您不需要主键来进行联接 table。您可以只在迁移文件上指定 id: false
。
还要注意更改数据库参数 () 而不告诉 rails schema 这些更改,您可以通过 dry 运行 rake db:migrate
你最后要找的是 HABTM 关系。 has_and_belongs_to_many: http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_and_belongs_to_many
您只需要:
student = Student.find(1)
student.courses
跟进。将一个单一的、自动递增的列作为永远不会被使用的主键,不太合适。 composite_primary_keys gem 确实影响了 ActiveRecord 中的 eager_load 机制并解决了这个问题。我们没有考虑它,因为我们不需要 gem 的主要功能,但它确实以最可接受的方式解决了这个问题。