2

我在 SQL 方面不是那么好,我遇到了一个我不知道如何解决的问题。我已经阅读并重新阅读了一本关于 SQL 的书(O'Reilly's Learning SQL)的部分内容,我希望其中包含我需要的信息,但我还没有找到。

我的问题如下。我将使用一个简化的示例使其更易于讨论。

我有三张桌子,car,rim 和两者的组合:carRim。

car
    carId
    description

rim
    rimId
    description

carRim
    carRimId
    carId
    rimId
    price

在表 carRim 中,我有一个额外的 price 属性,因为每种类型的汽车的轮辋价格可能不同。我的一个限制是,每种类型的轮辋只能与每种类型的汽车耦合一次。所以车圈的所有组合都应该是独一无二的。

如果我想为汽车添加轮辋,我需要一个尚未与该汽车耦合的轮辋列表。为此,我认为我需要轮辋表和 carRim 表分别用于轮辋的总列表和已经耦合到我要添加轮辋的汽车的 carRims 列表。

我编写了(简单)查询来制作与特定汽车耦合的轮辋列表,以下示例中,汽车 ID 为 9。

SELECT
    * 
FROM
    rims 
INNER JOIN
    carRims 
ON
    carRims.rimId = rim.rimId 
WHERE
    carRims.carId = 9 

但现在我需要尚未与特定汽车耦合的轮辋列表。问题是,如果我进行 LEFT OUTER JOIN,我得到的列表会被轮辋与其他汽车的耦合“污染”,因此“WHERE carRims.carId IS NULL”的过滤条件不起作用。

SELECT
    * 
FROM
    rims 
LEFT OUTER JOIN
    carRims 
ON
    carRims.rimId = rim.rimId 
WHERE
    carRims.carId IS NULL

另一个挑战是我不能使用 MySQL 5 的任何新语法,比如子查询,因为我的客户端使用的是 MySQL 4,此时无法升级。

是否可以在 MySQL 4 中编写针对此问题的查询,我怀疑可以。

谢谢!

4

3 回答 3

2
SELECT  *
FROM    rims r
WHERE   NOT EXISTS
        (
        SELECT  NULL
        FROM    carRims cr
        WHERE   cr.rimId = r.RimId
                AND cr.carID = 9
        )

更新:

要将 重写NOT EXISTS为 a LEFT JOIN / IS NULL,您需要将所有条件放入ON连接的子句中:

SELECT  r.* 
FROM    rims r
LEFT OUTER JOIN
        carRims cr 
ON      cr.rimId = r.rimId 
        AND cr.crID = 9
WHERE   cr.carId IS NULL
于 2010-01-25T15:29:58.703 回答
1

您可以在左外连接ON表达式中添加额外的条件。您想要的是与您的行carRims匹配的rims行,并且属于 9 号车,对吗?

SELECT
    * 
FROM
    rims 
LEFT OUTER JOIN
    carRims 
ON
    carRims.rimId = rim.rimId 
    AND carRims.carId = 9
WHERE
    carRims.carId IS NULL

如果其他汽车有其他carRims,它们将被子ON句中的额外条件过滤掉。


关于在条款JOINWHERE条款中放置条件的位置的问题:

对于连接,放置比较的位置很重要。JOIN 子句中的条件用于测试一个表中的行是否与另一个表中的行匹配。在我们测试匹配之后,我们对各个表中的行做什么取决于连接的类型。

对于外连接,我们希望行来自,rims即使carRims. 如果我们将 carID=9 条件放在WHERE子句中会怎样?

FROM rims r LEFT OUTER JOIN carRims c ON r.rimId = c.rimID
WHERE c.carID = 9

以下是发生的情况:外连接返回来自 的所有行rims,其中的行与carRims匹配,rimID并且包括具有虚假carID值的行。只有当没有一辆汽车与给定的轮辋匹配时,它才会对c.*列使用 NULL。

但随后该WHERE子句消除了连接产生的所有行,除非carID值为 9。这意味着它还消除了 where carIDis NULL,即它消除了任何 rim 不匹配任何汽车的行。因此,结果变得等同于 a 的结果INNER JOIN

因此,我们需要在将carRims行加入之前从错误的 carID中排除行rims

FROM rims r LEFT OUTER JOIN carRims c ON r.rimId = c.rimID AND c.carID = 9

这是该单个表中的哪些行可以匹配另一个联接表中的行的条件。

ON许多书籍说您可以在从句和从句之间自由混合条件WHERE。但这并非在所有情况下都是正确的。它适用于INNER JOIN,因为无论哪种方式最终结果都是相同的。它也适用于仅适用于左连接中的左表的条件,例如:

FROM rims r LEFT OUTER JOIN carRims c ON r.rimId = c.rimID AND r.make = 'ABC Rims'

FROM rims r LEFT OUTER JOIN carRims c ON r.rimId = c.rimID 
WHERE r.make = 'ABC Rims'

对于左外连接中右表条件,放置条件的位置很重要。


对你所说的再发表评论:

我的一个限制是,每种类型的轮辋只能与每种类型的汽车耦合一次。所以车圈的所有组合都应该是独一无二的。

那么您是否声明了UNIQUE对 的约束carRims(carId,rimId)

于 2010-01-25T15:34:19.790 回答
0

(我需要更多的空间来详细说明,评论没有足够的字符)

@Bill Karwin 您提供的查询是我的问题的正确答案。谢谢!

我想要的是连接到特定汽车的行(轮辋)列表。

在“Learning SQL”一书中,我读到查询中过滤器和连接条件的位置无关紧要。他们给出了一个内部连接的示例,其中有 1 个连接条件(在 ON 子句之后)和 1 个过滤条件(在 WHERE 子句之后)。他们证明,无论您将这两个条件放在 ON 子句之后还是 WHERE 子句之后,都没有关系。

考虑到这一点,我不理解您解决方案的一部分。如果条件

carRims.carId = 9

和条件

carRims.carId IS NULL

两者都存在,这不会导致结果集为空吗?因为没有字段可以同时为 9 和 NULL 对吗?

我的怀疑是:JOIN 子句的条件是受位置限制的,您不能只将它们放在 WHERE 子句中。

它是否正确?

于 2010-01-25T15:58:35.863 回答