【问题标题】:SQL problems when migrating from MySQL to PostgreSQL从 MySQL 迁移到 PostgreSQL 时的 SQL 问题
【发布时间】:2012-11-09 21:14:38
【问题描述】:

我有一个 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"。我该如何解决这个问题?

【问题讨论】:

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


    【解决方案1】:

    问题一:

    ...有更好的方法吗?

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

    当primary时允许查询目标列表中的非GROUP BY列 键在 GROUP BY 子句中指定 (Peter Eisentraut)

    问题2:

    以下语句...产生错误

    PG::Error: ERROR: 缺少表“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 先验。如果我对丢失表定义的假设成立的话,这应该会快得多(除了正确之外)。

    【讨论】:

    • 谢谢,但我是否需要将所有查询重新编码为纯 SQL,还是可以继续使用 Spot.paginate
    • @TomSöderlund:我不是 Ruby 专家,不知道 spot.paginate。新查询比旧查询简单得多。 EXISTS 是非常基础的标准 SQL ...
    【解决方案2】:

    走“纯 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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-03
      • 2014-05-31
      • 2012-05-17
      相关资源
      最近更新 更多