Добро пожаловать,
У меня проблема с оптимизацией базы данных Oracle. Я хочу найти некоторые объекты, соответствующие тексту, который вводит пользовательский интерфейс в поле поиска. Я использую PHP с Doctrine в своем приложении, и Doctrine генерирует код, подобный следующему:
SELECT p0_."BD" AS BD --...
FROM "P" p0_
LEFT JOIN "G" g1_ ON p0_.GID = g1_.ID
LEFT JOIN "PD" p2_ ON p0_.PDID = p2_.ID
LEFT JOIN "PI" p3_ ON p2_.ID = p3_.PDID --ONE TO MANY, SO OPERATOR 'IN' REQUIRED
LEFT JOIN "IT" i4_ ON i4_.ID = p3_.ITID
LEFT JOIN "A" a5_ ON p0_.AID = a5_.ID
LEFT JOIN "P" p6_ ON p0_.MPID = p6_.ID
LEFT JOIN "RS" r7_ ON p0_.RSID = r7_.ID
WHERE (((
LOWER(p2_."FN") LIKE '%abc%'
OR LOWER(p2_."GN") LIKE '%abc%'
OR LOWER(a5_."SN") LIKE '%abc%'
OR LOWER(a5_."CN") LIKE '%abc%'
OR LOWER(a5_."CON") LIKE '%abc%'))
OR p2_."ID" IN
(SELECT p8_."ID"FROM "PI" p9_
LEFT JOIN "PD" p8_
ON p9_.PDID = p8_.ID
WHERE LOWER(p9_."VALUE") LIKE '%abc%'
))
AND p6_."ID" = p0_."ID";
База данных довольно большая (~ полмиллиона строк), и системе требуется около 40 секунд, чтобы вернуть результат.
«Объяснить план» в SQL Developer показывает, что система использует HASH-соединения с полным доступом к таблице. Использование вложенных циклов ничего не меняет (даже с индексами).
Можно ли как-то оптимизировать этот процесс?
РЕДАКТИРОВАТЬ
Следующий запрос возвращает тот же результат, но он намного быстрее (1,5 с):
SELECT p0_."BD" AS BD --...
FROM "P" p0_
LEFT JOIN "G" g1_ ON p0_.GID = g1_.ID
LEFT JOIN "PD" p2_ ON p0_.PDID = p2_.ID
LEFT JOIN "PI" p3_ ON p2_.ID = p3_.PDID
LEFT JOIN "IT" i4_ ON i4_.ID = p3_.ITID
LEFT JOIN "A" a5_ ON p0_.AID = a5_.ID
LEFT JOIN "P" p6_ ON p0_.MPID = p6_.ID
LEFT JOIN "RS" r7_ ON p0_.RSID = r7_.ID
WHERE
p0_.id IN
(
SELECT p0_A.id FROM "P" p0_A JOIN "PD" p2_A ON (p0_A.PDID = p2_A.ID AND (LOWER(p2_."GN") LIKE '%abc%' OR LOWER(p2_."FN") LIKE '%abc%'))
UNION
SELECT p0_B.id FROM "P" p0_B LEFT JOIN "PD" p2_B ON p0_B.PDID = p2_B.ID JOIN "PI" p3_B ON (p2_B.ID = p3_B.PDID AND(LOWER(p3_B."VALUE") LIKE '%abc%'))
--UNION ANOTHER SELECTS
)
AND p6_."ID" = p0_."ID";
Хитрость заключается в том, чтобы добавить дополнительное условие в ON части запроса и выполнить JOIN вместо LEFT JOIN. Это должен быть подзапрос, потому что трюк будет работать только с полями одной таблицы. К сожалению, Doctrine не поддерживает клаузулу UNION. Оракул так глуп с его механизмом оптимизации или я что-то упустил? Может быть, я мог бы как-то изменить запрос?
Greathings!
не будучи в состоянии проверить код, это может быть то, что вы после?
SELECT P0_."BD" AS BD --...
FROM "P" P0_
LEFT JOIN "G" g1_ ON p0_.GID = g1_.ID
LEFT JOIN "PD" P2_ ON P0_.PDID = P2_.ID
LEFT JOIN "PI" p3_ ON p2_.ID = p3_.PDID
LEFT JOIN "IT" I4_ ON I4_.ID = P3_.ITID
LEFT JOIN "A" a5_ ON p0_.AID = a5_.ID
LEFT JOIN "P" P6_ ON P0_.MPID = P6_.ID
LEFT JOIN "RS" r7_ ON p0_.RSID = r7_.ID
LEFT JOIN (SELECT P0_A.ID FROM "P" P0_A JOIN "PD" P2_A ON (P0_A.PDID = P2_A.ID AND (LOWER(P2_."GN") LIKE '%abc%' OR LOWER(P2_."FN") LIKE '%abc%'))) XX ON P0_.ID = XX.ID
LEFT JOIN (SELECT P0_B.ID FROM "P" P0_B LEFT JOIN "PD" P2_B ON P0_B.PDID = P2_B.ID JOIN "PI" P3_B ON (P2_B.ID = P3_B.PDID AND(LOWER(P3_B."VALUE") LIKE '%abc%'))) YY ON P0_.ID = YY.ID
WHERE P6_."ID" = P0_."ID"and (XX.ID is not null or XX.ID is not null);
Других решений пока нет …