将使用 GROUP BY 的查询从 MySQL 转换为 Postgres 和 SQLite

Posted

技术标签:

【中文标题】将使用 GROUP BY 的查询从 MySQL 转换为 Postgres 和 SQLite【英文标题】:Translate query with GROUP BY from MySQL to Postgres and SQLite 【发布时间】:2016-04-07 11:24:33 【问题描述】:

mysql 中,我使用了很多如下所示的结构:

LEFT JOIN(
    SELECT field_one, field_two, field_three FROM (
        SELECT field_one, field_two, field_three FROM table_one
        ORDER BY field_three 
    ) o GROUP BY field_one
) abc ON cur_table.some_field = abc.field_one

我使用它,因为它可以帮助我完成一项日常任务——在分组之前对一些数据进行排序。现在我想知道如何将这个 SQL 结构从 MySQL 迁移到 SQLitePostgreSQL

【问题讨论】:

用样本数据准备sqlfiddle.com。提供准确的答案会容易得多 为我工作(在 SQLite 中)。你为什么不试试呢? 我没有尝试过,因为我在 Postgresql 中的某个地方有红色,例如,他们以其他方式做类似的事情(这对我来说看起来相当丑陋)。所以我认为它不标准。 派生表中的order by 没有意义,并且服务器没有实际用途,因为查询中中间步骤的行的“顺序”无关紧要。另外:) o group by field_one 是无效的 SQL(您不能“分组”表别名)。另外:group by 没有任何意义,因为您没有使用任何聚合。我不明白你想用这个实现什么。您要解决的根本问题是什么?你为什么不直接加入这两个表呢?无用的order by和错误的group by是什么原因? @a_horse_with_no_name。假设field_three 是一些auto_increment 字段,field_one 是外键,它指向当前表中的字段some_field(在我的示例中具有别名cur_table)。现实世界的任务是获取一些重要数据(来自 field_two),当前表中每个some_field 一个。任务可能是获取table_one 中第一个或最后一个插入的数据。因此,我确实需要order by,它确实有道理,而且我确实需要分组,而且我不需要任何聚合。 【参考方案1】:

我怀疑您查询的目的是使用 MySQL 怪癖为现有 GROUP BY 子句中也未列出的未聚合列选择任意行(这 违反 SQL 标准 em> 并且大多数其他 RDBMS 不支持)。通过在子查询中排序,您可以让 MySQL 选择每个组中具有最小 field_three 的行,所以我假设您想要:

每个field_one 的最小field_three 和同一行中的field_two 一起使用。

您的原始查询在此处遵循 SQL 标准的 Postgres 中工作。如果SELECTGROUP BY 子句,则必须列出或聚合所有输出列。考虑:

How do I port query with GROUP BY clause to PostgreSQL?

带窗口函数的标准 SQL

一种可能的标准SQL解决方案是在子查询中使用窗口函数row_number()

SELECT field_one, field_two, field_three
FROM  (
   SELECT field_one, field_two, field_three
        , row_number() OVER(PARTITION BY field_one ORDER BY field_three) rn
   FROM   table_one
   ) sub
WHERE sub.rn = 1

适用于 Postgres,但不适用于不支持窗口函数的 SQLite 或 MySQL。

基本标准 SQL

此查询适用于所有三个 RDBMS(几乎也适用于其他任何地方),但需要 field_three 中的唯一最大值(如果每个 field_one 存在最大 field_three 的关系,则返回多行)。

SELECT t1.*
FROM   table_one t1
LEFT   JOIN table_one t2 ON t1.field_one = t2.field_one
                        AND t1.field_three < t2.field_three
WHERE  t2.field_one IS NULL

如果您有任何独特的(一组)列,则可以解决平局,但这很笨拙。相关:

Fetch the row which has the Max value for a column

Postgres

(除了支持所有标准 SQL 解决方案之外)Postgres 还具有强大的 DISTINCT ON(标准 DISTINCT 的扩展,但不反对像 MySQL 和 SQLite 怪癖这样的标准):

SELECT DISTINCT ON (field_one)
       field_one, field_two, field_three
FROM   table_one
ORDER  BY field_one, field_three

您可以通过向ORDER BY 添加更多列来解决平局。详情:

Select first row in each GROUP BY group?

SQLite

... 与 MySQL 有类似的怪癖(违反 SQL 标准)。 From the release notes:

查询形式:“SELECT max(x), y FROM table”返回包含最大 x 值的同一行上的 y 值。

所以:

SELECT field_one, field_two, max(field_three) AS field_three
FROM   table_one
GROUP  BY field_one

field_two 取自 max(field_three) 的行。相关:

How can I select the set of rows where each item has the greatest timestamp?

加入和加入条件在任何地方都一样:

LEFT JOIN (SELECT ...) abc ON cur_table.some_field = abc.field_one

【讨论】:

+1,你也许可以通过 APPLY 操作击败 row_number() 解决方案……但它仍然不受 MySql 支持。 谢谢您,先生!您最后的评论是否意味着我的问题的解决方案将在 SQLitePostgres 以及可能在其他数据库中工作,尽管正如 a_horse_with_no_name 和 wildplasser 所说,group by 将从查询计划中删除?跨度> 附言。您正确理解了我查询的确切目的。 @Jacobian:您的原件在 Postgres 中不起作用。考虑上面添加的解释。

以上是关于将使用 GROUP BY 的查询从 MySQL 转换为 Postgres 和 SQLite的主要内容,如果未能解决你的问题,请参考以下文章

使用函数 SUM() 和 Group by 将 Mysql 查询转换为 SQL 查询

group by 深入总结(转)

mysql 从聚合函数group by到sql_mode

[Mysql 查询语句]——分组查询group by

MySQL 查询优化与 group by 和 order by rand

模拟 group by,order by,从 mysql 到 ms sql 2000 的限制