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 (...)

ここで、2 つの列リスト ( CardId) と ( Id) には共通の列がありません。つまり、自然結合には結合するものがなく、通常はデカルト結合になります。ただし、 where 句は、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;

さて、このクエリを見ると、テーブルに対して 2 つのクエリを UNIONing していることがわかります。2Contains番目のクエリは、これらの行のサブセットです。定義により、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 を実行したときに、明示的な列修飾子を削除する必要があります。削除してみてください

WHERE Costs.CardId = rc.Id AND ManaCardId IN

コストとRC

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

含むおよび tmp

いずれにしても、Natural Join を使用せずにこの SQL を書き直すことができます。

于 2013-11-18T07:19:18.083 に答える