将 SQL 查询从 with 子句转移到没有 with 子句

Posted

技术标签:

【中文标题】将 SQL 查询从 with 子句转移到没有 with 子句【英文标题】:Transferring SQL query from with clause to without with clause 【发布时间】:2021-12-14 03:47:43 【问题描述】:

考虑查询:

查找总工资大于所有部门总工资平均值的所有部门

with dept_total (dept_name, value) as
(
    select dept_name, sum(salary)
    from instructor
    group by dept_name
),
dept_total_avg(value) as
(
    select avg(value)
    from dept_total
)
select dept_name
from dept_total, dept_total_avg
where dept_total.value >= dept_total avg.value;

在不使用 with 构造的情况下重写此查询。

查询基于由 Korth 的数据库系统概念提供的大学模式。我假设我只需要考虑讲师表即可找到查询的答案。

讲师(ID、姓名、部门名称、薪水)

我可以找到所有部门总工资的平均值

SELECT AVG(salary) GROUP BY dept_name;

然后我输了。我没有找到继续的方法。

我找到了that。但我正在寻找更多解释,因为我无法从这个链接中理解它。

感谢您的帮助。

【问题讨论】:

Bad habits to kick : using old-style JOINs - 旧式 逗号分隔的表格列表 样式已替换为 ANSI 中的 proper ANSI JOIN 语法-92 SQL 标准(差不多 30 年前),不鼓励使用它 【参考方案1】:

以下是在不支持 CTE 的 mysql 版本中执行此操作的几种方法(我假设这就是他们让您重写查询以省略 with 的原因)。此外,您正在按部门计算平均工资,但问题是要求找到每个部门的平均 TOTAL 工资,然后返回高于该平均工资的那些。

https://dbfiddle.uk/?rdbms=mysql_5.5&fiddle=fefea829cff3e20aea93634f9a4114d6

使用子查询:

SELECT dept_name
FROM (
  SELECT dept_name, sum(salary) as salaries
  FROM instructor GROUP BY dept_name
) d

WHERE salaries > (
  SELECT avg(salaries) as avgSalaries FROM (
    SELECT dept_name, sum(salary) as salaries
    FROM instructor GROUP BY dept_name
  ) z
)

加入:

SELECT dept_name
FROM (
  SELECT dept_name, sum(salary) as salaries
  FROM instructor GROUP BY dept_name
) d
CROSS JOIN (
  SELECT avg(salaries) as avgSalaries FROM (
    SELECT dept_name, sum(salary) as salaries
    FROM instructor GROUP BY dept_name
  ) z
) avgs
WHERE salaries > avgs.avgSalaries

使用会话变量(在以后的版本中已弃用,但仍然有效):

SELECT avg(salaries) INTO @avg FROM (
  SELECT dept_name, sum(salary) as salaries
  FROM instructor GROUP BY dept_name
) z;

SELECT @avg;

SELECT dept_name
FROM instructor GROUP BY dept_name
HAVING sum(salary) > @avg;

所有好的技术都可以理解。

【讨论】:

我会选择 INNER JOIN ... ON,而不是 CROSS JOIN ... WHERE。 我也是,可能。我认为它们在优化器中可能会出现相同的结果,但我想我想强调的是,由于您要加入一行结果集,所以加入条件并不重要,我正在使用结果只是作为比较器。可能应该只是这么说而不是那样写。我不确定我最后一次真正使用交叉连接是什么时候。 用子查询解决方案d和z代表什么? 在 mySQL 中,每个派生表都需要一个别名,因此 d 和 z 只是嵌套选择语句的别名。如果要从嵌套选择中进行选择,则必须为其命名。这些只是方便。我选择 d 作为第一个,因为它是 dept_name 的第一个字母,而另一个是 z,因为通常我从 z 开始,如果我不知道马上要嵌套多少层,我会在字母表中向后工作。 d 和 z 在这里可以是任何东西。你可以使用任何别名。【参考方案2】:

看了你上一个问题,发现你用的是mysql5.8,那么就可以使用分析功能了

select distinct t1.dept_name
  from (
select t1.*,
       sum(salary) over(partition by dept_name) val,
       sum(salary) over() / count(distinct dept_name) over() avg
  from Instructor t1
) t1
 where t1.val > t1.avg

【讨论】:

以上是关于将 SQL 查询从 with 子句转移到没有 with 子句的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server - 在 INSERT 语句中使用 WITH 子句

JOOQ:使用 WITH 子句创建子查询(使用纯 SQL)

在 PHP 中使用 SQL “WITH”子句

如何在 Spark SQL 中的 WITH 子句中缓存子查询结果

SQL:with 查询

ClickHouse高级数据查询SQL: WITH/JOIN/IN/INTO OUTFILE/嵌套子查询/交并差计算等