使用 activerecord 为特定用户查找不是多对多关系的记录 (ruby-on-rails)
Use activerecord to find records that are not in many-to-many relation for specific user (ruby-on-rails)
好的,我的数据库包含三个table:用户、单词、状态(这是一个关系table,没有ID)。每个用户可以有很多词,每个词可以为不同的用户重复(有些用户和词可以不关联)。每对用户词都有状态(真或假)记录在状态 table.
数据库:
users statuses words
| id | | user_id | | id |
| name | | word_id | | word |
| | | status: boolean | | |
我想创建一个网页,其中包含当前用户没有状态的所有词。
型号:
class User < ActiveRecord::Base
has_many :statuses
has_many :words, through: :statuses
end
class Status < ActiveRecord::Base
belongs_to :user
belongs_to :word
validates :word, uniqueness: {scope: :user}
end
class Word < ApplicationRecord
has_and_belongs_to_many :users
end
我写了一个请求,它工作正常,但它很丑,我觉得不对
def words_without_status
sql = "SELECT words.* FROM words
LEFT JOIN (SELECT status.* FROM status
LEFT JOIN users ON users.id = status.user_id
WHERE (status.user_id = #{current_user.id})) AS tmp
ON words.id = tmp.word_id
WHERE tmp.word_id IS NULL
ORDER BY id"
@words = ActiveRecord::Base.connection.execute(sql)
end
这是 returns 所有具有 current_user 状态的单词的代码,但我需要相反的代码。
@words = current_user.words
谢谢。
请检查以下查询:
查找 current_user 的词,使其状态为 false-
@words = current_user.statuses.includes(:words).where("status : false")
免责声明:您实际上并没有使用您的 status.status
布尔列。假设这只是一个不必要的信息(因此“没有状态的单词”意味着status
table中实际上没有记录), 你的初始 sql 看起来不错,但也许我们可以设计出更优雅的东西。
假设您首先尝试获取对特定用户具有任何状态的词的 ID:
SELECT status.word_id FROM status WHERE (status.user_id = #{current_user.id})
获取所有没有状态的单词是一个相反的操作,所以你可以这样做:
SELECT words.* WHERE words.id NOT IN (
SELECT status.word_id FROM status WHERE (status.user_id = #{current_user.id})
)
如果您更喜欢 activerecordish 方法,它可以写成:
@words = Word.where("id NOT IN (SELECT status.word_id FROM status WHERE (status.user_id = :user_id))", user_id: current_user.id)
使用 LEFT JOINS
而不是 NOT IN
可能是另一种选择,但我省略了这一点让您发现。应针对每种特定情况分别测量实际性能差异。
有两种方法:使用连接或子查询。
有连接的看起来像
Word.includes(:statuses => :word).where(statuses: { user: current_user, status: true })
如果您想使用子查询,那么:
Word.where(id: Status.select(:word_id).where(status: true, user: current_user))
我建议您在数据集上对这两种方法进行基准测试,然后选择速度更快的一种。
好的,我的数据库包含三个table:用户、单词、状态(这是一个关系table,没有ID)。每个用户可以有很多词,每个词可以为不同的用户重复(有些用户和词可以不关联)。每对用户词都有状态(真或假)记录在状态 table.
数据库:
users statuses words
| id | | user_id | | id |
| name | | word_id | | word |
| | | status: boolean | | |
我想创建一个网页,其中包含当前用户没有状态的所有词。
型号:
class User < ActiveRecord::Base
has_many :statuses
has_many :words, through: :statuses
end
class Status < ActiveRecord::Base
belongs_to :user
belongs_to :word
validates :word, uniqueness: {scope: :user}
end
class Word < ApplicationRecord
has_and_belongs_to_many :users
end
我写了一个请求,它工作正常,但它很丑,我觉得不对
def words_without_status
sql = "SELECT words.* FROM words
LEFT JOIN (SELECT status.* FROM status
LEFT JOIN users ON users.id = status.user_id
WHERE (status.user_id = #{current_user.id})) AS tmp
ON words.id = tmp.word_id
WHERE tmp.word_id IS NULL
ORDER BY id"
@words = ActiveRecord::Base.connection.execute(sql)
end
这是 returns 所有具有 current_user 状态的单词的代码,但我需要相反的代码。
@words = current_user.words
谢谢。
请检查以下查询: 查找 current_user 的词,使其状态为 false-
@words = current_user.statuses.includes(:words).where("status : false")
免责声明:您实际上并没有使用您的 status.status
布尔列。假设这只是一个不必要的信息(因此“没有状态的单词”意味着status
table中实际上没有记录), 你的初始 sql 看起来不错,但也许我们可以设计出更优雅的东西。
假设您首先尝试获取对特定用户具有任何状态的词的 ID:
SELECT status.word_id FROM status WHERE (status.user_id = #{current_user.id})
获取所有没有状态的单词是一个相反的操作,所以你可以这样做:
SELECT words.* WHERE words.id NOT IN (
SELECT status.word_id FROM status WHERE (status.user_id = #{current_user.id})
)
如果您更喜欢 activerecordish 方法,它可以写成:
@words = Word.where("id NOT IN (SELECT status.word_id FROM status WHERE (status.user_id = :user_id))", user_id: current_user.id)
使用 LEFT JOINS
而不是 NOT IN
可能是另一种选择,但我省略了这一点让您发现。应针对每种特定情况分别测量实际性能差异。
有两种方法:使用连接或子查询。
有连接的看起来像
Word.includes(:statuses => :word).where(statuses: { user: current_user, status: true })
如果您想使用子查询,那么:
Word.where(id: Status.select(:word_id).where(status: true, user: current_user))
我建议您在数据集上对这两种方法进行基准测试,然后选择速度更快的一种。