第1部分。
当你自然连接时,已经“自然连接”的列会丢失它们的表别名,例如:
SELECT CardId, Block
FROM Contains
NATURAL JOIN
(SELECT CardId FROM ...
) tmp
WHERE Contains.CardId = tmp.CardId
这里自然连接的两边共享一个列CardId
,所以不能引用该列的表别名,例如:
SELECT CardId, Block
FROM Contains
NATURAL JOIN
(SELECT CardId FROM ...
) tmp
WHERE CardId = CardId
但显然这是没有意义的,因为自然连接意味着CardId
=CardId
根据定义,所以上面应该是简单的:
SELECT CardId, Block
FROM Contains
NATURAL JOIN
(SELECT CardId FROM ...
) tmp
第2部分。
内部查询中的这种自然连接:
SELECT CardId
FROM Costs
NATURAL JOIN
(SELECT Id FROM ...
) rc
WHERE Costs.CardId = rc.Id
AND ManaCardId IN (...)
这里,两个列列表 ( CardId
) 和 ( Id
) 没有共同的列,这意味着自然连接没有要连接的内容 - 这通常会导致笛卡尔连接。但是,由于Costs.CardId = rc.Id
. 因此,为了使代码更清晰,我宁愿只使用内部连接:
SELECT CardId
FROM Costs
JOIN
(SELECT Id FROM ...
) rc
WHERE Costs.CardId = rc.Id
AND ManaCardId IN (...)
第 3 部分。
自然连接通常不受欢迎,因为它们取决于选择的列 - 因此,如果开发人员将列添加到选择列表但没有注意到它正在使用自然连接,它可能会产生意想不到的副作用。显式连接表通常是一种好习惯,例如:
SELECT Block FROM (
SELECT CardId, Block
FROM Contains
GROUP BY Block
UNION
SELECT CardId, Block
FROM Contains
JOIN
(SELECT CardId
FROM Costs
JOIN
(SELECT Id
FROM Card
WHERE RarityId IN (SELECT Id FROM Rarity WHERE RarityType='Legendary')
) rc
ON Costs.CardId = rc.Id
WHERE ManaCardId IN (SELECT Id FROM ManaCard WHERE ManaColor='Red')
) tmp
ON Contains.CardId = tmp.CardId
) bn
GROUP BY Block
HAVING COUNT(*) < 2;
您还可以简化最内层连接:
SELECT Block FROM (
SELECT CardId, Block
FROM Contains
GROUP BY Block
UNION
SELECT CardId, Block
FROM Contains
JOIN
(SELECT CardId
FROM Costs
JOIN Card rc
ON Costs.CardId = rc.Id
WHERE Costs.ManaCardId IN (SELECT Id FROM ManaCard WHERE ManaColor='Red')
AND rc.RarityId IN (SELECT Id FROM Rarity WHERE RarityType='Legendary')
) tmp
ON Contains.CardId = tmp.CardId
) bn
GROUP BY Block
HAVING COUNT(*) < 2;
现在,查看此查询,我注意到您正在对Contains
表进行两个查询联合 - 第二个查询是这些行的子集。根据定义,第 2 次查询返回的所有行都包含在第 1 次查询中,并且 UNION 消除了重复,因此上述查询在逻辑上等价于:
SELECT Block FROM (
SELECT CardId, Block
FROM Contains
GROUP BY Block
) bn
GROUP BY Block
HAVING COUNT(*) < 2;
我注意到带有 GROUP BY 的查询没有任何聚合,因此这在 Oracle 中不起作用。我认为这个查询相当于:
SELECT Block FROM (
SELECT DISTINCT Block
FROM Contains
) bn
GROUP BY Block
HAVING COUNT(*) < 2;
它从返回一组不同块的查询中计算重复块的数量!- 这意味着这个查询相当于:
SELECT DISTINCT Block FROM Contains;
我怀疑 PHP 运行此查询的方式与它在 Oracle 中的工作方式之间存在一些逻辑差异 - 所以上述简化可能是错误的。