2

我正在尝试编写一个 SQL 查询,该查询返回自 4 月 1 日以来有新发票但尚未安排今年秋季交货的客户的所有学生电子邮件地址。即使我知道有满足这些条件的条目,这也会返回一个空集。我已经尝试了一些不同的事情,但没有运气,有没有办法做到这一点?

SELECT clients.studentEmail 
FROM `clients`, `invoices` 
WHERE clients.clientId = invoices.clientId 
AND invoices.datePosted > "2013-04-01" 
AND NOT EXISTS 
    (SELECT * 
    FROM appointments, clients
    WHERE clients.clientId = appointments.clientId 
    AND appointments.serviceDirection = "Delivery" 
    AND appointments.date > '2013-07-01')
4

4 回答 4

4

您必须将not exists子查询与外部查询相关联。例如:

select  clients.studentemail 
from    clients c
join    invoices i
on      c.clientid = i.clientid 
where   invoices.dateposted > "2013-04-01" 
        and not exists 
        (
        select  * 
        from    appointments a
        where   c.clientid = a.clientid -- Relates outer to inner query
                and a.servicedirection = "delivery" 
                and a.date > '2013-07-01')
        )
于 2013-07-19T05:26:04.653 回答
3

我不确定您要返回什么结果集。但是在子查询中包含客户表看起来不正确。

我们通常想要的是一个相关的子查询。例如:

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。这将排除至少有一个匹配约会的所有行,所以我们剩下的行没有匹配。我们可以引用约会中保证不为空的任何列。(我们通常有一个idPRIMARY KEY 列(因此不是 NULL。)但我们也可以使用该clientID列,在这种情况下,因为每个匹配的行都保证不为空,因为它必须等于来自的 clientId客户表,并且 NULL 值永远不会“等于”任何其他值。(这是 JOIN 谓词中的相等条件保证我们(在查询中)a.clientId 不为空。

这种模式称为“反连接”。

于 2013-07-19T05:27:25.790 回答
0

除了知道正确答案之外,了解错误所在同样有益

NOT EXISTS 
  (SELECT * 
  FROM appointments, clients
  WHERE clients.clientId = appointments.clientId 
  AND appointments.serviceDirection = "Delivery" 
  AND appointments.date > '2013-07-01')

基本上意味着“所有客户都没有在'2013-07-01'之后的交货预约”。这个“ALL”字出现在这里的原因是因为你的子查询中的“client”与外部查询的客户端没有关系,因此被视为一个完整的表(即所有客户端)

于 2013-07-19T05:39:28.720 回答
0

我认为更有效的方法是使用连接而不是子查询/存在子句:

SELECT c.studentEmail 
  FROM `clients` c
    JOIN (select clientId from `invoices` i where i.datePosted > '2013-04-01') iclients
            ON c.clientId = iclients.clientId
    JOIN (select clientId from `appointments` a where a.serviceDirection = "Delivery" AND a.date > '2013-07-01') aclients
        ON c.clientId = aclients.clientId
于 2021-02-14T04:18:25.440 回答