我不确定您要返回什么结果集。但是在子查询中包含客户表看起来不正确。
我们通常想要的是一个相关的子查询。例如:
SELECT c.studentEmail
FROM `clients` c
JOIN `invoices` i
ON i.clientId = c.clientId
WHERE i.datePosted > '2013-04-01'
AND NOT EXISTS
( SELECT 1
FROM appointments a
WHERE a.clientId = c.clientId
AND a.serviceDirection = "Delivery"
AND a.date > '2013-07-01'
)
请注意,NOT EXISTS
子查询引用c.clientId
,它是来自外部查询中表clientId
列的值。clients
我们称之为“相关子查询”,因为对于外部查询返回的每一行,我们(有效地)运行子查询,并在子查询clientId
的谓词(WHERE 子句)中使用该行的 from。
NOT EXISTS 返回 TRUE(如果未找到匹配行)或 FALSE(如果找到至少一个匹配行)。
在性能方面,这种类型的查询对于大型集合来说可能是昂贵的,因为 MySQL 有效地为外部查询中返回的每一行运行一个单独的子查询。反连接模式通常(并不总是)更有效(有合适的索引可用)。
使用反连接模式获得等效结果的另一种方法:
SELECT c.studentEmail
FROM `clients` c
JOIN `invoices` i
ON i.clientId = c.clientId
LEFT
JOIN appointments a
ON a.clientId = c.clientId
AND a.serviceDirection = "Delivery"
AND a.date > '2013-07-01'.
WHERE i.datePosted > '2013-04-01'
AND a.clientId IS NULL
我们对约会表使用 LEFT JOIN 来查找匹配的行。请注意,所有用于查找匹配行的谓词都需要位于 ON 子句(而不是 WHERE 子句)中。
这将返回匹配的行,以及在appointments
.
现在的“技巧”是在 WHERE 子句中包含一个谓词,用于检查 a.clientID IS NULL。这将排除至少有一个匹配约会的所有行,所以我们剩下的行没有匹配。我们可以引用约会中保证不为空的任何列。(我们通常有一个id
PRIMARY KEY 列(因此不是 NULL。)但我们也可以使用该clientID
列,在这种情况下,因为每个匹配的行都保证不为空,因为它必须等于来自的 clientId客户表,并且 NULL 值永远不会“等于”任何其他值。(这是 JOIN 谓词中的相等条件保证我们(在查询中)a.clientId 不为空。
这种模式称为“反连接”。