我正在研究越来越深入的RoRails,并且停留在我们提供给我的数据上,以重现这个可爱的框架。 首先让我向您展示我的表格和模型助理。
create_table "skills", force: :cascade do |t|
t.string "name"
t.bigint "parent_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["parent_id"], name: "index_skills_on_parent_id"
end
create_table "skills_users", id: false, force: :cascade do |t|
t.bigint "skill_id", null: false
t.bigint "user_id", null: false
t.index ["skill_id", "user_id"], name: "index_skills_users_on_skill_id_and_user_id"
end
create_table "users", force: :cascade do |t|
t.integer "points"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
和型号:
class Skill < ApplicationRecord
validates :name, presence: true
has_many :children, class_name: "Skill", foreign_key: :parent_id
belongs_to :parent, class_name: "Skill", foreign_key: :parent_id, optional: true
has_and_belongs_to_many :users
end
class User < ApplicationRecord
validates :points, presence: true
has_and_belongs_to_many :skills
end
目标是对“技能”进行分类,获取“技能”子级并在每个父“技能”中计算用户的积分。这是数据示例
SKILLS
+-----------------------+
|ID|NAME |PARENT_ID|
+-----------------------+
|1 |Football | |
+-----------------------+
|2 |Basketball| |
+-----------------------+
|3 |Foot |1 |
+-----------------------+
|4 |Basket |2 |
+-----------------------+
|5 |Soccer |1 |
+-----------------------+
SKILLS_USERS
+-------------------+
|ID|SKILL_ID|USER_ID|
+-------------------+
|1 |1 |1 |
+-------------------+
|2 |1 |2 |
+-------------------+
|3 |3 |3 |
+-------------------+
|4 |2 |4 |
+-------------------+
|5 |5 |5 |
+-------------------+
USERS
+---------+
|ID|POINTS|
+---------+
|1 |100 |
+---------+
|2 |200 |
+---------+
|3 |100 |
+---------+
|4 |50 |
+---------+
|5 |10 |
+---------+
预期的请求应如下所示:
+--------------------------------+
|ID|NAME |POINTS|USERS_COUNT|
+--------------------------------+
|1 |Football |410 |4 |
+--------------------------------+
|2 |Basketball|50 |1 |
+--------------------------------+
我首先尝试使用以下查询在纯sql中回答它:
SELECT id , Name , count ( points) as POINTS , count (USER_ID ) as USERS_COUNT
FROM SKILLS
INNER JOIN SKILLS as SK ON id = SK.parent_id
INNER JOIN SKILLS_USERS AS su ON su.skill_id = id
INNER JOIN USERS AS User ON SU.USER_ID = User.id
但是似乎我在某个地方错了。
我认为ActiveRecord方式很有趣,红宝石是魔术。 ActiveRecord对联接表进行这种请求的方法是什么?我们仅选择父级Skills?
答案 0 :(得分:1)
如果要记录给定技能的每个用户的分数,则需要将其放在联接表上,而不是用户表上。
create_table "skills", force: :cascade do |t|
t.string "name"
t.bigint "parent_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["parent_id"], name: "index_skills_on_parent_id"
end
create_table "user_skills", force: :cascade do |t|
t.bigint "skill_id", null: false
t.bigint "user_id", null: false
t.integer "points"
t.index ["skill_id", "user_id"], name: "index_skills_users_on_skill_id_and_user_id"
end
create_table "users", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
您想使用has_many through:
而不是has_and_belongs_to_many
来建立关联,这不允许您直接查询联接表或访问该points
列。
class Skill < ApplicationRecord
has_many :user_skills
has_many :users, through: :user_skills
end
class UserSkill < ApplicationRecord
belongs_to :user
belongs_to :skill
end
class User < ApplicationRecord
has_many :user_skills
has_many :users, through: :user_skills
end
您的查询也比需要的复杂得多,但仍未达到要求。您可以通过查询技能表并加入user_skills
来获得所需的结果。
SELECT skills.id, skills.name,
SUM(user_skills.points) AS points,
count(user_skills.user_id) AS user_count
FROM "skills"
INNER JOIN "user_skills" ON "user_skills"."skill_id" = "skills"."id"
GROUP BY skills.id
我们可以在ActiveRecord中执行以下操作:
Skill.joins(:user_skills)
.select('skills.id, skills.name, SUM(user_skills.points) AS points, COUNT(user_skills.user_id) AS user_count')
.group('skills.id')
这将返回ActiveRecord::Relation
条记录中的Skill
条记录,并附带其他.points
和.user_count
属性。