在 WHERE 子句中使用 SELECT 语句

Posted

技术标签:

【中文标题】在 WHERE 子句中使用 SELECT 语句【英文标题】:Using a SELECT statement within a WHERE clause 【发布时间】:2011-09-19 10:32:23 【问题描述】:
SELECT * FROM ScoresTable WHERE Score = 
  (SELECT MAX(Score) FROM ScoresTable AS st WHERE st.Date = ScoresTable.Date)

在 WHERE 子句中是否有描述使用 SELECT 语句的名称?这是好还是坏的做法?

这会是更好的选择吗?

SELECT ScoresTable.* 
FROM ScoresTable INNER JOIN 
  (SELECT Date, MAX(Score) AS MaxScore 
  FROM ScoresTable GROUP BY Date) SubQuery 
  ON ScoresTable.Date = SubQuery.Date 
  AND ScoresTable.Score = SubQuery.MaxScore

它远没有那么优雅,但似乎比我以前的版本运行得更快。我不喜欢它,因为它在 GUI 中显示的不是很清楚(而且需要 SQL 初学者理解)。我可以将其拆分为两个单独的查询,但随后事情开始变得混乱......

注意我需要的不仅仅是日期和分数(例如姓名)

【问题讨论】:

看看窗口函数sqlbooks.ru/readarticle.aspx?part=02&file=sql200523 您假设他的数据库实现了这一点。此外,几乎可以肯定这里不需要窗口函数来满足 Jo 的需求。 确实,对于这个简单的示例,它们不是必需的,但它们为这些类型的查询的更复杂变体提供了优雅的解决方案,因此值得了解。 我同意 - 值得了解 - 对于显然对 SQL 很陌生的人来说,还有很多东西需要学习。 您的 EDIT 是一个更好的解决方案,我认为更优雅。正如您已经注意到的那样,它的性能也更高。它可能看起来更复杂,但初学者应该从一开始就学习编写好的代码,而不是先学习坏习惯。 【参考方案1】:

称为相关子查询。它有它的用途。

【讨论】:

谢谢@Mladen。在您看来,这是解决问题的最佳/最有效的解决方案吗? 不,不是。按照您执行此操作的方式,将为表中的每一行运行子查询。像你一样将它作为一个连接总是更好。【参考方案2】:

这根本不是坏习惯。它们通常被称为 SUBQUERYSUBSELECTNESTED QUERY

这是一个相对昂贵的操作,但是在处理数据库时遇到很多子查询是很常见的,因为这是对数据执行某种操作的唯一方法。

【讨论】:

@Gunner:是的。另外,子选择。 您通常可以将它们分解为一个 JOIN,但是 SQL Server 现在非常擅长自己找出最佳查询计划。尽管如此,编写最好的代码而不是依赖编译器优化并没有什么坏处。 是的。您可以通常将它们分解为连接。但是有些操作不能通过 JOIN 执行。例如 ANY/SOME/ALL 子查询操作... @WinstonSmith:我可以将我的示例分解为 JOIN 吗(或者这就是我在编辑中所做的?);恐怕我对精确术语的了解很差。 @Pablo:从查看其他人的 cmets 来看,我所拥有的似乎是一个 correlated 子查询。我对简单的子查询非常熟悉,但这是我第一次体验相关子查询。你能推荐一个更好的方法吗?【参考方案3】:

使用 SQL Server 的analytic (or windowing) functions,有一种更好的方法可以实现您想要的结果。

SELECT DISTINCT Date, MAX(Score) OVER(PARTITION BY Date) FROM ScoresTable

如果您需要的不仅仅是日期和最高分数组合,您可以使用排名函数,例如:

SELECT  *
FROM    ScoresTable t
JOIN (   
    SELECT 
        ScoreId,
        ROW_NUMBER() OVER (PARTITION BY Date ORDER BY Score DESC) AS [Rank] 
        FROM ScoresTable
) window ON window.ScoreId = p.ScoreId AND window.[Rank] = 1

如果您希望返回多条记录(如果它们共享相同的 MAX(Score)),则可能需要使用 RANK() 而不是 ROW_NUMBER()。

【讨论】:

感谢@WinstonSmith,但您的建议似乎比必要的复杂。这是否比我最初的建议提供更多? 无论如何它在访问上都不起作用,但如果你在某个时候转移到 SQL 服务器,请记住这一点。【参考方案4】:

子查询的原理一点也不差,但我认为你不应该在你的例子中使用它。如果我理解正确,您希望获得每个日期的最高分。在这种情况下,您应该使用 GROUP BY。

【讨论】:

由于他选择 *,看起来他想要的不仅仅是分数和日期组合。也许他需要弄清楚哪些学生获得了这些分数。 你是对的。幸运的是,在 mysql 中(虽然我不确定他是否正在使用它),他将能够选择未聚合的列或 group by 子句中的列。 你说得对,@WinstonSmith,我需要获取与分数相关的名称,所以简单的 GROUP BY 是不够的。不幸的是,我这次没有使用 MySQL,@Lex;我很惭愧地说我正在使用 Access :)【参考方案5】:

这是一个相关的子查询。

(这是一个“嵌套”查询 - 虽然这是一个非常非技术性的术语)

内部查询从外部查询中获取值 (WHERE st.Date = ScoresTable.Date),因此外部查询中的每一行都会计算一次。

还有一种不相关的形式,其中内部查询是独立的,因此只执行一次。

例如

 SELECT * FROM ScoresTable WHERE Score = 
   (SELECT MAX(Score) FROM Scores)

使用子查询并没有错,除非在不需要它们的地方:)

您的语句可以重写为聚合函数,具体取决于您在 select 语句中需要哪些列。

SELECT Max(score), Date FROM ScoresTable 
Group By Date

【讨论】:

如果您需要的不仅仅是 MAX(Score) 和 Date 对,那么聚合函数将不起作用。 你假设他需要更多 - 最好只编写代码以满足需求而不是引入不必要的复杂性。 我没有假设 - 从 SELECT * 开始的查询中可以明显看出。当然,这可能只是懒惰,但这也是一种假设! 现在我是一个更快乐的人,我已经删除了我的反对票! 正如其他地方评论的那样,@WinstonSmith 是对的,我需要的不仅仅是分数和日期。【参考方案6】:

在您的情况下,为什么不使用 GROUP BY 和 HAVING 子句而不是自己的 JOINING 表。您还可以使用其他有用的功能。 see this link

【讨论】:

GROUP BY 和 HAVING 在这种情况下不起作用,因为如原始问题所述:“我需要的不仅仅是日期和分数(例如姓名)”。如果您再次检查,您会发现问题中已经使用了别名。【参考方案7】:

子查询是名称。

有时它是必需的,但好/坏取决于它的应用方式。

【讨论】:

以上是关于在 WHERE 子句中使用 SELECT 语句的主要内容,如果未能解决你的问题,请参考以下文章

在mysql select语句中使用条件where子句

如何在光标的 select 语句 where 子句中传递逗号分隔值

SQL语句中,子句不能使用列别名问题

在 WHERE 子句中使用函数编写 SQL SELECT 语句是不是有 Django 等效项?

如何在 WHERE 子句中编写带有 SELECT 语句的 SQL DELETE 语句?

如何在带有 select 语句的 where 子句中使用比较运算符?