パート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 で動作する方法との間には、いくつかの論理的な違いがあると思われます。したがって、上記の単純化はおそらく間違っています。