不使用 first() 的 Spark SQL vs Normal SQL 查询错误

Posted

技术标签:

【中文标题】不使用 first() 的 Spark SQL vs Normal SQL 查询错误【英文标题】:Spark SQL vs Normal SQL query error without using first() 【发布时间】:2020-11-21 14:21:54 【问题描述】:

我试图在 Spark SQL 中运行一个简单的查询,但除非我使用 first(),否则它会抛出错误

此查询在 mysql 中正常工作

SELECT film.title,count(rental.rental_id) as total_rentals, film.rental_rate, count(rental.rental_id) * film.rental_rate as revenue
FROM rental
         INNER JOIN inventory ON rental.inventory_id = inventory.inventory_id
         INNER JOIN film ON inventory.film_id = film.film_id
GROUP BY film.title
ORDER BY 1

但 Spark SQL 不一样 我得到的错误是:

Exception in thread "main" org.apache.spark.sql.AnalysisException: expression 'film.`rental_rate`' is neither present in the group by, nor is it an aggregate function. Add to group by or wrap in first() (or first_value) if you don't care which value you get.;;

这样做实际上可以解决问题

SELECT  film.title,count(rental.rental_id) as total_rentals, first(film.rental_rate), count(rental.rental_id) * first(film.rental_rate) as revenue
FROM rental
INNER JOIN inventory ON rental.inventory_id = inventory.inventory_id
INNER JOIN film ON inventory.film_id = film.film_id
GROUP BY film.title
ORDER BY 1

有人能解释一下为什么需要 Spark SQL 吗?

【问题讨论】:

【参考方案1】:

SQL 中有一个共同要求,即group by 查询中的所有非聚合列都必须出现在group by 子句中。一些数据库理解功能依赖列的概念,并允许您将主键列仅放在 group by 子句中。

我猜title 不是film 的主键,所以你原来的查询不是有效的标准SQL。我怀疑您在 MySQL 中运行它,它(唉!)具有允许禁用标准要求的选项。

在支持group by 中的函数依赖的数据库中,您可以将查询分阶段为:

SELECT f.title, count(*) as total_rentals, f.rental_rate, count(*) * f.rental_rate as revenue
FROM rental r
INNER JOIN inventory i ON r.inventory_id = i.inventory_id
INNER JOIN film f ON i.film_id = f.film_id
GROUP BY f.film_id
ORDER BY 1

我认为 Spark 不会理解这一点,所以只需将所有需要的列添加到 group by 子句:

SELECT f.title, count(*) as total_rentals, f.rental_rate, count(*) * f.rental_rate as revenue
FROM rental r
INNER JOIN inventory i ON r.inventory_id = i.inventory_id
INNER JOIN film f ON i.film_id = f.film_id
GROUP BY f.film_id, f.title, f.rental_rate
ORDER BY 1

注意事项:

group by 子句中包含film_id 仍然是一个好习惯;在现实生活中,两部不同的电影可能具有相同的标题和评分,您不想将它们组合在一起

count(r.rental_id) 可以简化为count(*)(因为显然该列不能是null

表别名使查询更易于编写和阅读

【讨论】:

感谢您的快速反馈。那么对于 MySQL 等,它会在后台自动完成?【参考方案2】:

我怀疑你想要:

SELECT f.title, COUNT(*) as total_rentals, f.rental_rate,  
       SUM(f.rental_rate) as revenue
FROM rental r JOIN
     inventory i
     ON r.inventory_id = i.inventory_id JOIN
     film f
     ON i.film_id = f.film_id
GROUP BY f.title, f.rental_rate
ORDER BY 1;

注意事项:

一般来说,GROUP BY 列应该是SELECT 中的未聚合列。在过去的几年里,这甚至在 MySQL 中是必需的(使用默认设置)。 您可以对rental_rate 列求和。无需计数和乘法。 表别名使查询更易于编写和阅读。

第一个 SQL 在 MySQL 中起作用是因为 MySQL 扩展了 SQL 语法以允许它。 SparkSQL(在这种情况下)正在做几乎所有其他数据库所做的事情。

【讨论】:

我又向codereview.stackexchange.com/questions/252458/… 发布了一个问题,您能解释一下区别吗? @戈登林诺夫

以上是关于不使用 first() 的 Spark SQL vs Normal SQL 查询错误的主要内容,如果未能解决你的问题,请参考以下文章

Spark SQL 可以在 GROUP BY 聚合中使用 FIRST_VALUE 和 LAST_VALUE(但这不是标准的)

有spark大佬知道下面这个代码哪里可以优化吗?

如何为 Spark SQL 中的posexplode 列提供别名?

[Py]Spark SQL:使用框架的输入行约束窗口的每一帧

spark使用insertInto存入hive分区表中

在 spark.sql 的选择中使用 cast()