sql'替换在Rails 4.2中
我有一个需要一些连接/自定义查询的关联。 当试图找出如何实现这个重复的响应是finder_sql
。 然而在Rails 4.2(及以上)中:
ArgumentError:未知的键::finder_sql
我的查询来完成连接看起来像这样:
'SELECT DISTINCT "tags".*'
' FROM "tags"'
' JOIN "articles_tags" ON "articles_tags"."tag_id" = "tags"."id"'
' JOIN "articles" ON "article_tags"."article_id" = "articles"."id"'
' WHERE articles"."user_id" = #{id}'
我明白这可以通过以下方式实现:
has_many :tags, through: :articles
但是,如果联接的基数很大(即用户拥有数千篇文章 - 但系统只有一些标签),则需要加载所有文章/标签:
SELECT * FROM articles WHERE user_id IN (1,2,...)
SELECT * FROM article_tags WHERE article_id IN (1,2,3...) -- a lot
SELECT * FROM tags WHERE id IN (1,2,3) -- a few
当然也对一般情况感到好奇。
注意:也尝试使用proc语法,但似乎无法弄清楚:
has_many :tags, -> (user) {
select('DISTINCT "tags".*')
.joins('JOIN "articles_tags" ON "articles_tags"."tag_id" = "tags"."id"')
.joins('JOIN "articles" ON "article_tags"."article_id" = "articles"."id"')
.where('"articles"."user_id" = ?', user.id)
}, class_name: "Tag"
ActiveRecord :: StatementInvalid:PG :: UndefinedColumn:错误:列tags.user_id不存在
SELECT DISTINCT“tags”。* FROM“tags”JOIN“articles_tags”ON“articles_tags”。“tag_id”=“tags”。“id”JOIN“articles”ON“article_tags”。“article_id”=“articles”。“id “WHERE”标签“。”“user_id”= $ 1 AND(“articles”。“user_id”= 1)
这看起来好像是在尝试将user_id
自动注入标签(并且该列只存在于文章中)。 注意:我为多个用户预加载,所以无法使用user.tags
而没有其他修复(粘贴的SQL就是我所看到的那样!)。 思考?
虽然这并没有直接解决你的问题 - 如果你只需要你的数据的一个子集,你可以通过一个子查询预加载它:
users = User.select('"users".*"').select('COALESCE((SELECT ARRAY_AGG(DISTINCT "tags"."name") ... WHERE "articles"."user_id" = "users"."id"), '{}') AS tag_names')
users.each do |user|
puts user[:tag_names].join(' ')
end
以上是针对Postgres的数据库(由于ARRAY_AGG
),但其他数据库可能存在同等的解决方案。
另一种选择可能是将视图设置为假连接表(需要数据库支持):
CREATE OR REPLACE VIEW tags_users AS (
SELECT
"users"."id" AS "user_id",
"tags"."id" AS "tag_id"
FROM "users"
JOIN "articles" ON "users"."id" = "articles"."user_id"
JOIN "articles_tags" ON "articles"."id" = "articles_tags"."article_id"
JOIN "tags" ON "articles_tags"."tag_id" = "tags"."id"
GROUP BY "user_id", "tag_id"
)
然后,您可以使用has_and_belongs_to_many :tags
(未测试 - 可能希望设置为readonly
并且可以删除某些连接并在使用适当的外键约束设置时使用)。
所以我的猜测是,当你尝试访问@user.tags
时,你会得到错误,因为你在user.rb
有该关联。
所以我认为会发生什么是当我们尝试访问@user.tags
,我们试图获取用户的tags
,并且导轨会搜索其user_id
与当前提供的用户标识匹配的Tags
。 由于Rails默认将关联名称作为modelname_id
格式,即使您没有user_id
它也会尝试在该列中搜索,并且无论您希望如何,都会搜索(或添加WHERE "tags"."user_id"
)或不是因为最终目标是找到属于当前用户的tags
。
当然,我的回答可能无法100%解释。 随意评论你的想法或如果你发现任何错误,让我知道。
简答
好吧,如果我理解正确,我认为我有解决方案,它只是使用核心ActiveRecord实用程序,并不使用finder_sql。
可能会使用:
user.tags.all.distinct
或者,在用户模型中将has_many标签更改为
has_many :tags, -> {distinct}, through: :articles
您可以在用户中创建一个帮助器方法来检索它:
def distinct_tags
self.tags.all.distinct
end
证据
从你的问题我相信你有以下情况:
考虑到这一点,我创建了以下迁移:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name, limit: 255
t.timestamps null: false
end
end
end
class CreateArticles < ActiveRecord::Migration
def change
create_table :articles do |t|
t.string :name, limit: 255
t.references :user, index: true, null: false
t.timestamps null: false
end
add_foreign_key :articles, :users
end
end
class CreateTags < ActiveRecord::Migration
def change
create_table :tags do |t|
t.string :name, limit: 255
t.timestamps null: false
end
end
end
class CreateArticlesTagsJoinTable < ActiveRecord::Migration
def change
create_table :articles_tags do |t|
t.references :article, index: true, null:false
t.references :tag, index: true, null: false
end
add_index :articles_tags, [:tag_id, :article_id], unique: true
add_foreign_key :articles_tags, :articles
add_foreign_key :articles_tags, :tags
end
end
和模型:
class User < ActiveRecord::Base
has_many :articles
has_many :tags, through: :articles
def distinct_tags
self.tags.all.distinct
end
end
class Article < ActiveRecord::Base
belongs_to :user
has_and_belongs_to_many :tags
end
class Tag < ActiveRecord::Base
has_and_belongs_to_many :articles
end
接下来使用大量数据对数据库进行种子处理:
10.times do |tagcount|
Tag.create(name: "tag #{tagcount+1}")
end
5.times do |usercount|
user = User.create(name: "user #{usercount+1}")
1000.times do |articlecount|
article = Article.new(user: user)
5.times do |tagcount|
article.tags << Tag.find(tagcount+usercount+1)
end
article.save
end
end
最后在rails console中:
user = User.find(3)
user.distinct_tags
导致以下输出:
Tag Load (0.4ms) SELECT DISTINCT `tags`.* FROM `tags` INNER JOIN `articles_tags` ON `tags`.`id` = `articles_tags`.`tag_id` INNER JOIN `articles` ON `articles_tags`.`article_id` = `articles`.`id` WHERE `articles`.`user_id` = 3
=> #<ActiveRecord::AssociationRelation [#<Tag id: 3, name: "tag 3", created_at: "2016-10-18 22:00:52", updated_at: "2016-10-18 22:00:52">, #<Tag id: 4, name: "tag 4", created_at: "2016-10-18 22:00:52", updated_at: "2016-10-18 22:00:52">, #<Tag id: 5, name: "tag 5", created_at: "2016-10-18 22:00:52", updated_at: "2016-10-18 22:00:52">, #<Tag id: 6, name: "tag 6", created_at: "2016-10-18 22:00:52", updated_at: "2016-10-18 22:00:52">, #<Tag id: 7, name: "tag 7", created_at: "2016-10-18 22:00:52", updated_at: "2016-10-18 22:00:52">]>
链接地址: http://www.djcxy.com/p/36687.html