【问题标题】:Postgresql error with Rails 3 using order("RANDOM()")使用 order("RANDOM()") 的 Rails 3 的 Postgresql 错误
【发布时间】:2012-05-27 16:23:58
【问题描述】:

我正在尝试在我的数据库中查询与当前查看的记录(基于标记)相似的记录,但我想随机化顺序。

我的开发环境是mysql,所以我会这样做:

@tattoos = Tattoo.tagged_with(tags, :any => true).order("RAND()").limit(6)

可行,但我的生产环境是使用 postgresql 的 heroku,所以我尝试使用它:

@tattoos = Tattoo.tagged_with(tags, :any => true).order("RANDOM()").limit(6)

但我收到以下错误:

ActionView::Template::Error (PGError: ERROR: for SELECT DISTINCT, ORDER BY 表达式必须出现在选择列表中

SELECT  DISTINCT tattoos.* FROM "tattoos" JOIN taggings 
tattoos_taggings_color_fantasy_newschool_nerdy_tv_477  ON 
tattoos_taggings_color_fantasy_newschool_nerdy_tv_477.taggable_id = tattoos.id AND 
tattoos_taggings_color_fantasy_newschool_nerdy_tv_477.taggable_type = 'Tattoo' WHERE 
(tattoos_taggings_color_fantasy_newschool_nerdy_tv_477.tag_id = 3 OR 
tattoos_taggings_color_fantasy_newschool_nerdy_tv_477.tag_id = 4 OR 
tattoos_taggings_color_fantasy_newschool_nerdy_tv_477.tag_id = 5 OR 
tattoos_taggings_color_fantasy_newschool_nerdy_tv_477.tag_id = 24 OR 
tattoos_taggings_color_fantasy_newschool_nerdy_tv_477.tag_id = 205) ORDER BY RANDOM() LIMIT 6):

【问题讨论】:

  • 快速评论一下,对于超过几千行的数据集,按 random() 限制 x 排序是非常低效的。以至于一旦你有 10k 到 1M 行,它就会变得太慢而无法接受。

标签: ruby-on-rails ruby-on-rails-3 postgresql heroku


【解决方案1】:

在更仔细地分析查询后,我必须更正我的初稿。查询将需要 DISTINCTGROUP BY

(可能)重复的tattoos.* 来自第一次连接到表taggings 中的(可能)多行。然后,您的查询引擎会尝试使用 DISTINCT 再次删除此类重复项 - 以一种语法上非法的方式。

DISTINCT 基本上按结果列从左到右对结果行进行排序,并为每组重复项选择第一个。这就是为什么最左边的ORDER BY 列必须匹配SELECT 列表。

MySQL 比较宽松,允许非标准使用DISTINCT,但 PostgreSQL 会抛出错误。

ORM 经常产生无效的 SQL 语句(毕竟它们只是拐杖)。但是,如果您使用适当的 PostgreSQL 库,则不应一开始就产生这样的非法语句。我不是 Ruby 专家,但这里有些问题。

查询也很丑陋,效率低下

有几种方法可以修复它。例如:

SELECT * 
FROM  (<query without ORDER BY and LIMIT>) x
ORDER  BY RANDOM()
LIMIT  6

或者,更好的是,重写查询,使用这种更快、更简洁的替代方法做同样的事情:

SELECT ta.*
FROM   tattoos ta
WHERE  EXISTS (
    SELECT 1
    FROM   taggings t
    WHERE  t.taggable_id = ta .id
    AND    t.taggable_type = 'Tattoo'
    AND    t.tag_id IN (3, 4, 5, 24, 205)
    )
ORDER  BY RANDOM()
LIMIT  6;

您必须自己在 Ruby 中实现它。

【讨论】:

  • 如何从该查询中删除DISTINCT?好像postgresql是自动添加的。
  • @rugbert:我不是 Ruby 方面的专家,但我提供了一个改进的答案,包括解释和查询替代方案。
  • 很酷,谢谢,我可以使用它。问题 tho,您如何使用“ta”而不是标签从标签表中提取数据?
  • @rugbert:很酷。 :) 通过在此处分配表别名:FROM tattoos ta - 这是FROM tattoos AS ta 的缩写。 More details in the excellent manual here.
【解决方案2】:

不确定随机,因为它应该可以工作。 不过注意http://railsforum.com/viewtopic.php?id=36581

有可能适合你的代码

/lib/agnostic_random.rb
module AgnosticRandom
  def random
    case DB_ADAPTER
      when "mysql" then "RAND()"
      when "postgresql" then "RANDOM()"
    end
  end
end

/initializers/extend_ar.rb (name doesn't matter)
ActiveRecord::Base.extend AgnosticRandom

【讨论】:

  • 是的,我看到了那个帖子,但我仍然会收到 postgresql 错误。另外,我与那个模块和我的 aws gem 有冲突
猜你喜欢
  • 1970-01-01
  • 2014-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多