将自然连接与Where操作一起使用 [英] Using Natural Join with Where operation

查看:185
本文介绍了将自然连接与Where操作一起使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个查询,该查询已在phpMyAdmin中使用,并且运行良好,但是我将数据库迁移到其他服务器上,现在使用SQL * Plus执行查询.查询现在正在生成

I have a query that I've been using in phpMyAdmin and it's been working perfectly however I migrated my database over to different server and I am now using SQL*Plus to perform my queries. The query is now generating

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使用相同的功能,但是都无法正常工作.

Since it is not liking my qualifiers is there a different way to get the query without natural join? I've tried using the same thing with Join and Inner Join but both are not working.

推荐答案

第1部分.

自然联接时,自然联接"的列将失去表别名,例如:

When you natural join, the columns that have been "naturally joined" lose their table aliases, so for example:

SELECT CardId, Block
FROM Contains
NATURAL JOIN
   (SELECT CardId FROM ...
   ) tmp
WHERE Contains.CardId = tmp.CardId

此处,自然联接的两侧共享一个列CardId,因此您不能引用该列的表别名,例如:

Here, both sides of the natural join share a column CardId, so you cannot refer to the table alias for this column, e.g.:

SELECT CardId, Block
FROM Contains
NATURAL JOIN
   (SELECT CardId FROM ...
   ) tmp
WHERE CardId = CardId

但是显然这没有任何意义,因为自然连接意味着按照定义CardId = CardId,因此上述内容应该很简单:

But obviously this makes no sense, since the natural join means that CardId = CardId by definition, so the above should be simply:

SELECT CardId, Block
FROM Contains
NATURAL JOIN
   (SELECT CardId FROM ...
   ) tmp

第2部分.

内部查询中的自然连接:

This natural join in the inner query:

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子句仍然有效地进行了内部联接.因此,为了使代码更清晰,我宁愿仅使用内部联接:

Here, the two column lists (CardId) and (Id) have no columns in common, which means the natural join has nothing to join - which would normally result in a cartesian join. However, the where clause effectively does an inner join anyway because of Costs.CardId = rc.Id. So, just to make the code clearer, I would prefer to just use an inner join:

SELECT CardId
FROM Costs
JOIN
   (SELECT Id FROM ...
   ) rc
WHERE Costs.CardId = rc.Id
AND ManaCardId IN (...)

第3部分.

自然联接通常不受欢迎,因为它们取决于选择的列-因此,如果开发人员将一列添加到选择列表中,但没有注意到它使用的是自然联接,则可能会有意外的副作用.通常,最好明确地连接表,例如:

Natural joins are generally frowned upon because they are dependent on which columns are selected - so if a developer adds a column to a select list but doesn't notice it's using a naturaly join, it may have unexpected side effects. It is generally good practice to join tables explicitly, e.g.:

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;

您还可以简化最里面的联接:

You can also simplify the innermost join:

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消除重复项,因此上述查询在逻辑上等效于:

Now, looking at this query I notice that you're UNIONing two queries on the Contains table - the second query being a subset of those rows. By definition, all the rows returned by the 2nd query are included in the 1st query, and UNION eliminates duplicates, so the above query is logically equivalent to:

SELECT Block FROM ( 
   SELECT CardId, Block
   FROM Contains
   GROUP BY Block
) bn
GROUP BY Block
HAVING COUNT(*) < 2;

我注意到带有GROUP BY的查询没有任何聚合,因此这在Oracle中不起作用.我认为此查询等效于:

I note that the query with the GROUP BY doesn't have any aggregates, so this will not work in Oracle. I think this query is equivalent to:

SELECT Block FROM ( 
   SELECT DISTINCT Block
   FROM Contains
) bn
GROUP BY Block
HAVING COUNT(*) < 2;

从返回一个不同的块集的查询中计算出重复块的数量! -这意味着该查询等同于:

Which counts the number of duplicate blocks from a query that returns a distinct set of Blocks! - which means this query is equivalent to:

SELECT DISTINCT Block FROM Contains;

我怀疑PHP运行此查询的方式与其在Oracle中的工作方式之间存在逻辑上的差异-因此上述简化可能是错误的.

I suspect there are some logical differences between the way that PHP runs this query vs. how it will work in Oracle - so the above simplification is probably wrong.

这篇关于将自然连接与Where操作一起使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆