为什么这个SQL查询有效?

时间:2016-05-28 11:49:32

标签: mysql

这是来自wikibooks的练习,#6

表架构为enter image description here

问题是:

  

对于每件作品,找到该作品最昂贵的产品,并包括作品名称,供应商名称和价格(请注意,可能有两家供应商以最昂贵的价格提供相同的作品)。

解决方案:

SELECT Pieces.Name, Providers.Name, Price
   FROM Pieces INNER JOIN Provides ON Pieces.Code = Piece
               INNER JOIN Providers ON Providers.Code = Provider
   WHERE Price =
   (
     SELECT MAX(Price) FROM Provides
     WHERE Piece = Pieces.Code
   );

我不明白子查询部分。我认为找到最高价格的典型方法是

 SELECT MAX(Price) FROM Provides
 group by piece;

这样,我不能在SUBQUERY中使用price = SUBQUERY或者价格,而在解决方案中的子查询看起来像

SELECT MAX(Price) FROM Provides, pieces where provides.piece=pieces.code;

它只返回最大的数字,我无法弄清楚为什么它可以“分组”并返回正确的行。

3 个答案:

答案 0 :(得分:2)

尽管托马斯确实提供了一个更简单的解决方案,但让我们回到原来的问题,为什么它会起作用。

SELECT Pieces.Name, Providers.Name, Price
   FROM Pieces INNER JOIN Provides ON Pieces.Code = Piece
               INNER JOIN Providers ON Providers.Code = Provider
   WHERE Price =
   (
     SELECT MAX(Price) FROM Provides
     WHERE Piece = Pieces.Code
   );

首先,我讨厌在这个答案中使用相关子查询。相关子查询是针对EACH记录处理子查询一次的子查询。请注意,查询的外部部分提供了“Pieces”表引用。所以内部查询是从“PROVIDES”表中说的,给我当前“Pieces.code”值的最大价格。之后,它是一个简单的连接到其他表来获取片段和提供者的详细信息。

我个人的偏好是在所有具有自己的group by的代码的“提供”表上执行预聚合子查询ONCE。这会运行一次查询,分组,因此每个代码只有一条记录。通常,您会看到这样的查询以防止更大的开销。此外,最好使用别名,特别是如果您使用别名处理ex。

from LongTableNamesInYourDatabase LTN

无论何时使用多个表,总是尝试提供table.column或alias.column,以便将来试图帮助您的其他人知道特定列的来源而不仅仅是猜测。

SELECT 
      P.Name, 
      Prov.Name, 
      MaxByPiece.MaxPrice
   FROM
      ( SELECT 
              Pr1.Piece,
              MAX( Pr1.Price ) as MaxPrice
           FROM 
              Provides Pr1
           group by
              Pr1.Piece ) as MaxByPiece
         JOIN Provides Pr2 on MaxByPiece.Piece = Pr2.Piece AND MaxByPiece.MaxPrice = Pr2.Price
           JOIN Pieces P on Pr2.Piece = P.Code
           JOIN Providers Prov on Pr2.Provider = Prov.Code

它看起来可能看起来更复杂,但是如果你有多个表为给定的东西(合同,订单,人,销售代表,等等)多个行,并且最终会得到笛卡尔结果并且想知道为什么计数或总计中的重复。

第一个from子句查询除了获得每个部分的最大价格之外什么也没做,我使用别名Pr1来区分它与下一个连接。之后的连接是提供,所以我们可以找到该价格的所有件。请记住,问题需要所有提供商以最高价格。所以现在,我的记录符合每件和服务提供商的最高价格。所以我通过加入那些查找表来完成,这样我就可以获得名称

答案 1 :(得分:1)

  

我无法弄清楚为什么它可以'分组'并返回正确的行

使用组和MAX聚合函数也是一种可行的解决方案。

此:

SELECT Pieces.Name, Providers.Name, MAX(Price)
   FROM Pieces INNER JOIN Provides ON Pieces.Code = Piece
               INNER JOIN Providers ON Providers.Code = Provider
GROUP BY Piece

将在MySQL上工作并返回相同的结果。

相关子查询解决方案基本上实现了同样的目的,但表达方式不同。但是"我的"解决方案不会在许多RDBMS上工作,因为SELEcT中的列与GROUP BY中的列不同。它在MySQL中是允许的。

也许他们更喜欢指定子查询解决方案,因为它是标准的。在实际情况下,开发人员最常选择GROUP BY方法,而其他RDBMS将添加使其工作所需的列

,这有点奇怪。

答案 2 :(得分:1)

WHERE子句因此起作用:所有行都来自FROM子句(在您的情况下是Pieces-Provide-Providers组合),检查WHERE中的条件是否为真;只有在这种情况下才能保留行。

WHERE Price =
(
  SELECT MAX(Price) FROM Provides
  WHERE Piece = Pieces.Code
)

在这里,您可以获取Pieces-Provide-Providers行,使用其Pieces.Code并从Provide获取所有匹配项。然后你从这些中获取最高价格。您将此值与您的Pieces-Provide-Providers行的价格进行比较。如果它是相同的(即如果您的连接行具有件代码的最大提供价格),那么您保留该行。