Uso de Natural Join with Where operation

Tengo una consulta que he estado usando en phpMyAdmin y ha estado funcionando perfectamente, sin embargo migré mi database a un server diferente y ahora estoy usando SQL * Plus para realizar mis consultas. La consulta ahora está generando

ERROR at line 10: ORA-25155: column used in NATURAL join cannot have qualifier 

Aquí está mi consulta:

 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; 

Como no me gustan mis calificadores, ¿hay alguna forma diferente de get la consulta sin una combinación natural? Intenté usar lo mismo con Join e Inner Join, pero ambos no funcionan.

Parte 1.

Cuando se une de forma natural, las columnas que se han "unido naturalmente" pierden sus alias de tabla, por ejemplo:

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

Aquí, ambos lados de la combinación natural comparten una columna CardId , por lo que no puede hacer reference al alias de la tabla para esta columna, por ejemplo:

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

Pero obviamente esto no tiene sentido, ya que la unión natural significa que CardId = CardId por definición, por lo que lo anterior debería ser simplemente:

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

Parte 2.

Esta unión natural en la consulta interna:

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

Aquí, las dos lists de columnas ( CardId ) y ( Id ) no tienen columnas en común, lo que significa que la unión natural no tiene nada para join, lo que normalmente daría como resultado una unión cartesiana. Sin embargo, la cláusula where efectivamente hace una unión interna de todos modos debido a Costs.CardId = rc.Id Entonces, solo para hacer el código más claro, preferiría simplemente usar una combinación interna:

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

Parte 3.

Naturalmente, las combinaciones son desaprobadas porque dependen de qué columnas se selectn; por lo tanto, si un desarrollador agrega una columna a una list de selección pero no nota que usa una combinación natural, puede tener efectos secundarios inesperados. En general, es una buena práctica unir tablas explícitamente, por ejemplo:

 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; 

También puedes simplificar la unión más interna:

 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; 

Ahora, al observar esta consulta, noto que está UNIENDO dos consultas en la tabla Contains : la segunda consulta es un subset de esas filas. Por definición, todas las filas devueltas por la segunda consulta se incluyen en la primera consulta, y UNION elimina las duplicadas, por lo que la consulta anterior es lógicamente equivalente a:

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

Noto que la consulta con GROUP BY no tiene agregados, por lo que esto no funcionará en Oracle. Creo que esta consulta es equivalente a:

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

¡Lo que count la cantidad de bloques duplicates de una consulta que devuelve un set distinto de bloques! – lo que significa que esta consulta es equivalente a:

 SELECT DISTINCT Block FROM Contains; 

Sospecho que hay algunas diferencias lógicas entre la forma en que PHP ejecuta esta consulta y cómo funcionará en Oracle, por lo que la simplificación anterior probablemente sea incorrecta.

A medida que realiza la unión NATURAL, debe eliminar el calificador explícito de la columna. Por favor, intente revmocve

DONDE Costs.CardId = rc.Id AND ManaCardId IN

Costos y RC

DONDE contiene.CardId = tmp.CardId) bn

Contiene y tmp

En cualquier caso, puede volver a escribir este SQL sin Natural Joins.