我有一个查询,我一直在phpMyAdmin中使用它并且它已经完美地工作但是我将我的数据库迁移到不同的服务器,我现在使用SQL * Plus来执行我的查询。查询现在正在生成
ERROR at line 10:
ORA-25155: column used in NATURAL join cannot have qualifier
这是我的问题:
SELECT Block FROM (
SELECT CardId, Block
FROM Contains
GROUP BY Block
UNION
SELECT CardId, Block
FROM Contains
NATURAL JOIN
(SELECT CardId
FROM Costs
NATURAL JOIN
(SELECT Id
FROM Card
WHERE RarityId IN (SELECT Id FROM Rarity WHERE RarityType='Legendary')
) rc
WHERE Costs.CardId = rc.Id
AND ManaCardId IN (SELECT Id FROM ManaCard WHERE ManaColor='Red')
) tmp
WHERE Contains.CardId = tmp.CardId
) bn
GROUP BY Block
HAVING COUNT(*) < 2;
由于不喜欢我的限定符,有没有不同的方法来获得没有自然连接的查询?我尝试过使用Join和Inner Join相同的东西,但两个都没有用。
答案 0 :(得分:0)
当您执行NATURAL Join时,您应该删除显式列限定符。 请尝试revmocve
WHERE Costs.CardId = rc.Id和ManaCardId IN
费用和rc
WHERE Contains.CardId = tmp.CardId)bn
包含和tmp
在任何情况下,您都可以在没有自然连接的情况下重写此SQL。
答案 1 :(得分:0)
第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
,where子句无论如何都能有效地进行内连接。所以,为了使代码更清晰,我宁愿只使用内连接:
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
表上进行了两次查询 - 第二个查询是这些行的子集。根据定义,第二个查询返回的所有行都包含在第一个查询中,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中的工作方式之间存在一些逻辑差异 - 因此上述简化可能是错误的。