从MySQL迁移到PostgreSQL时出现SQL问题

时间:2012-11-09 21:14:38

标签: sql ruby-on-rails postgresql heroku group-by

我有一个Ruby on Rails 2.3.x应用程序,我正在尝试从我自己的VPS迁移到Heroku,包括从SQLite(开发)和MySQL(生产)移植到Postgres。

这是我正在使用的典型Rails调用:

spots = Spot.paginate(:all, :include => [:thing, :user, :store, {:thing => :tags}, {:thing => :brand}], :group => :thing_id, :order => order, :conditions => conditions, :page => page, :per_page => per_page)

问题1:我收到很多错误,例如PG::Error: ERROR: column "spots.id" must appear in the GROUP BY clause or be used in an aggregate function。 SQLite / MySQL在这里显然更加宽容。当然,我可以通过将指定的字段添加到我的:group参数中来轻松解决这些问题,但我觉得我搞砸了我的代码。还有更好的方法吗?

问题2:如果我输入Postgres缺少的所有GROUP BY列,我最终会得到以下语句(只有:group已更改):

spots = Spot.paginate(:all, :include => [:thing, :user, :store, {:thing => :tags}, {:thing => :brand}], :group => 'thing_id,things.id,users.id,spots.id', :order => order, :conditions => conditions, :page => page, :per_page => per_page)

这反过来产生以下SQL代码:

SELECT * FROM (SELECT DISTINCT ON ("spots".id) "spots".id, spots.created_at AS alias_0 FROM "spots"  
LEFT OUTER JOIN "things" ON "things".id = "spots".thing_id 
WHERE (spots.recommended_to_user_id = 1 OR spots.user_id IN (1) OR things.is_featured = 't')  
GROUP BY thing_id,things.id,users.id,spots.id) AS id_list 
ORDER BY id_list.alias_0 DESC LIMIT 16 OFFSET 0;

...产生错误PG::Error: ERROR: missing FROM-clause entry for table "users"。我该如何解决这个问题?

2 个答案:

答案 0 :(得分:4)

问题1:

......有更好的方法吗?

是。从PostgreSQL 9.1开始,表的主键在逻辑上涵盖了GROUP BY子句中表的所有列。我引用release notes for version 9.1

  

在主要时,允许查询目标列表中的非GROUP BY列   key在GROUP BY子句中指定(Peter Eisentraut)

问题2:

以下语句......产生错误

  

PG ::错误:错误:表“users”缺少FROM子句条目

我该如何解决这个问题?

首先(一如既往!),我格式化了您的查询,以便更容易理解。罪魁祸首有大胆的重点:

SELECT *
FROM  (
   SELECT DISTINCT ON (spots.id)
          spots.id, spots.created_at AS alias_0
   FROM   spots  
   LEFT   JOIN things ON things.id = spots.thing_id 
   WHERE (spots.recommended_to_user_id = 1 OR
          spots.user_id IN (1) OR
          things.is_featured = 't')  
   GROUP  BY thing_id, things.id, users.id, spots.id
   ) id_list 
ORDER  BY id_list.alias_0 DESC
LIMIT  16
OFFSET 0;

现在一切都很明显,对吧? 好吧,不是所有。还有很多。 DISTINCT ONGROUP BY在同一个查询中,有一个用途,但不在此处。 从根本上简化到:

SELECT s.id, s.created_at AS alias_0
FROM   spots s
WHERE  s.recommended_to_user_id = 1 OR
       s.user_id = 1 OR
       EXISTS (
          SELECT 1 FROM things t
          WHERE  t.id = s.thing_id
          AND    t.is_featured = 't')
ORDER  BY s.created_at DESC
LIMIT  16;

EXISTS半联接避免了以后GROUP BY先验的需要。这应该快得多(除了正确) - 如果我对丢失的表定义的假设成立。

答案 1 :(得分:0)

走“纯SQL”路线为我打开了一堆蠕虫,所以我尝试保留will_paginate gem并调整Spot.paginate参数。 :joins参数非常有用。

目前这对我有用:

spots = Spot.paginate(:all, :include => [:thing, {:thing => :tags}, {:thing => :brand}], :joins => [:user, :store, :thing], :group => 'thing_id,things.id,users.id,spots.id', :order => order, :conditions => conditions, :page => page, :per_page => per_page)