0

我有一个一直在 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 使用相同的东西,但两者都不起作用。

4

2 回答 2

1

第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 中的工作方式之间存在一些逻辑差异 - 所以上述简化可能是错误的。

于 2013-11-18T08:04:06.217 回答
0

当您执行 NATURAL Join 时,您应该删除显式列限定符。请尝试 revocve

其中 Costs.CardId = rc.Id AND ManaCardId IN

成本和费用

WHERE Con​​tains.CardId = tmp.CardId)bn

包含和 tmp

在任何情况下,您都可以在没有自然连接的情况下重写此 SQL。

于 2013-11-18T07:19:18.083 回答