执行此嵌套子查询的次数是多少次?
SELECT CID, CNAME
FROM CUSTOMER
WHERE EXISTS ( SELECT CID
FROM RENTALS
WHERE CUSTOMER.CID = RENTALS.CID
AND PICKUP = 'CARY' )
这是一个理论问题,即可以在我的书中找到。提供的答案是6,但我不明白为什么会这样。
好吧,我认为这本书本身存在一些问题。我会仔细阅读这本书,然后再提问。
答案 0 :(得分:2)
这个问题没有正确的理论答案。智能查询优化器可以将查询转换为JOIN:
SELECT CID, CNAME
FROM Customer
LEFT JOIN (SELECT DISTINCT CID FROM Rentals WHERE PICKUP = 'CARY') as Rentals
ON Customer.CID = Rentals.CID
WHERE Rentals.CID IS NOT NULL
现在子查询只执行一次。
答案 1 :(得分:2)
正如其他人所指出的,这个相关的子查询可以被重写为连接,但这并不完全是完整的故事,因为未转换的EXISTS的执行计划看起来非常像连接。所以这不是一个语法问题,而是一个查询优化问题。
EXISTS实际上只是“加入到这个数据集但只有一行中的一行即使有1,000,000个匹配”的语法简写,或者也称为semijoin。
因此,EXISTS谓词针对相关或不相关子查询所需的半连接可以通过多种方式实现,这在很大程度上取决于两个表中的数字或记录。
如果您认为CUSTOMER估计有一行,并且优化器估计RENTALS中有数千行PICKUP ='CARY',那么优化器很可能从CUSTOMER中读取该行对RENTALS表进行表单并执行单个查找。
如果在RENTALS表中估计有一百万个CUSTOMERS且只有一行,那么执行计划就会疯狂 - 优化者可以通过引导RENTALS表并查看CUSTOMER表来反转连接要返回的行。在这种情况下,子查询可能根本没有被执行过。
在这些极端之间存在各种其他优化。例如,将RENTAL.CID列的唯一值构建到PICKUP ='CARY'的行的内存中哈希表中,并全面扫描CUSTOMER TABLE以探测每行的哈希表,这将是一个HASH SEMIJOIN 。同样,没有执行可识别的子查询。 (并且Barmer建议的查询重写可能会导致这种计划,但也可能会限制优化者看到适合其他数据分布和基数的其他计划)。
所以,正如其他答案所说,问题确实没有实际意义,因为我认为这里有两个重要的教训:
第二点很重要,因为它反对某些开发人员本能避免编写相关子查询(特别是EXISTS或NOT EXISTS),以期提供自己的优化。特别是,考虑到正确/错误的数据分布,用外连接替换EXISTS可能非常不理想。
要直接回答问题,我要说:
答案 2 :(得分:1)
查询不会以这种方式执行。查询规划器会将其转换为对另一个的结果运行一个查询而不是一遍又一遍地运行子查询的事情。
您的查询的查询计划很可能与您进行左连接并检查现有记录的查询相同:
select c.CID, c.CNAME
from CUSTOMER c
left join RENTALS r on c.CID = r.CID and r.PICKUP = 'CARY'
where r.CID is not null