【问题标题】:Rewriting WITH statements into subquery statements in SQL?将 WITH 语句重写为 SQL 中的子查询语句?
【发布时间】:2015-06-29 15:16:05
【问题描述】:

我有以下两种关系:

Game(id, name, year)
Devs(pid, gid, role)

Game.id 是主键,Devs.gid 是 Game.id 的外键。

previous post I made here 中,另一位用户非常友好地帮助我创建了一个查询,以查找由制作该游戏的最多开发者制作的所有游戏。他的回答使用了 WITH 语句,我对这些不是很熟悉,因为我只有几周的时间学习 SQL。这是有效的查询:

WITH GamesDevs (GameName, DevsCount)
AS
(
    SELECT Game.name AS GameName, count(DISTINCT Devs.pid) AS DevsCount
    FROM Game, Devs
    WHERE Devs.gid=Game.id
    GROUP BY Devs.gid, Game.name
)

SELECT * FROM GamesDevs WHERE GamesDevs.DevsCount = (SELECT MAX(DevsCount) FROM GamesDevs)

为了更加熟悉 SQL,我尝试使用子查询而不是 WITH 语句来重写此查询。我一直在使用this Oracle documentation 来帮助我解决这个问题。我尝试像这样重写查询:

SELECT *
FROM (SELECT Game.name AS GameName, count(DISTINCT Devs.pid) AS DevsCount
    FROM Game, Devs
    WHERE Devs.gid=Game.id
    GROUP BY Devs.gid, Game.name) GamesDevs
WHERE GamesDevs.DevsCount = (SELECT MAX(DevsCount) FROM GamesDevs)

据我所知,这两个查询应该是相同的。但是,当我尝试运行第二个查询时,出现错误

消息 207,级别 16,状态 1,第 6 行无效的列名称“DevsCount”。

有谁知道我为什么会收到这个错误,或者为什么这两个查询不一样?

【问题讨论】:

  • 您使用的是哪个 DBMS?
  • 我在微软 Azure 云上工作!
  • 您需要在最后一个 from 子句中复制该子查询
  • 你说你是 SQL 新手?好,那么你不应该有太多的撤销:Bad habits to kick: using old-style joins

标签: sql azure-sql-database with-statement


【解决方案1】:

您需要在最后一个 from 子句中复制该子查询,例如:

SELECT *
FROM (SELECT Game.name AS GameName, count(DISTINCT Devs.pid) AS DevsCount
    FROM Game, Devs
    WHERE Devs.gid=Game.id
    GROUP BY Devs.gid, Game.name) GamesDevs
WHERE GamesDevs.DevsCount = (SELECT MAX(DevsCount) FROM (SELECT Game.name AS GameName, count(DISTINCT Devs.pid) AS DevsCount
    FROM Game
    INNER JOIN Devs ON Devs.gid=Game.id
    GROUP BY Devs.gid, Game.name))

但最好这样做:

SELECT TOP 1 WITH TIES Game.name AS GameName, count(DISTINCT Devs.pid) AS DevsCount
FROM Game
INNER JOIN Devs ON Devs.gid=Game.id
GROUP BY Devs.gid, Game.name
ORDER BY DevsCount DESC

【讨论】:

  • @Turophile - 你确定吗?据我所知,GROUP 在逻辑上是在 SELECT 之前处理的,所以在这个方向上不应该有任何依赖关系。
  • 谢谢!我确实发现在最后一行,我需要将其更改为 GROUP BY DEVS.gid, GAME.name) GamesDevs) 才能使其正常工作。为什么子查询需要重复呢?
  • @Damien_The_Unbeliever 我以为是这种情况,但我在多年前学习 SQL 并且从那时起有许多语法更改和新的 SQL 变体。另外,我可能完全错了。我将删除我的评论以避免混淆任何人。
【解决方案2】:

问题出在这一行:

WHERE GamesDevs.DevsCount = (SELECT MAX(DevsCount) FROM GamesDevs)

从 CTE 中选择两次。但是你不能从一个子查询。 第一条语句

WHERE GamesDevs.DevsCount 

是正确的。但是

(SELECT MAX(DevsCount) FROM GamesDevs)

不正确,因为您可以重用子查询。从类似于视图的 cte 中进行选择,这就是为什么您可以同时使用最大值并比较计数的原因

【讨论】:

    【解决方案3】:

    您可以在子查询中使用RANK 窗口函数来定位每个游戏中不同开发者数量最多的记录:

    SELECT GameName, DevsCount
    FROM (
       SELECT Game.name AS GameName, 
              COUNT(DISTINCT Devs.pid) AS DevsCount,
              RANK() OVER (ORDER BY COUNT(DISTINCT Devs.pid) DESC) AS rnk
       FROM Game
       INNER JOIN Devs ON Game.id = Devs.gid
       GROUP BY Devs.gid, Game.name ) t
    WHERE t.rnk = 1
    

    这样您就不必重复SELECT MAX(DevsCount) 查询。您只需选择那些具有rnk = 1 的记录。

    您还应该考虑使用ANSI-standard SQL 来指定您正在使用的JOIN 操作的确切类型。

    @Giorgi 建议使用SELECT TOP 1 WITH TIES 的答案是对您最初问题的更清洁的解决方案。我只是将这篇文章作为更正您正在使用的子查询的替代方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-01
      • 1970-01-01
      • 2014-10-13
      • 1970-01-01
      相关资源
      最近更新 更多