#sql #interbase
Есть три таблицы main (id, f1, f2, f3, ...) detail (id, main_id) sub_detail (id, param, val) Связь main -> detail один ко многим, detail -> sub_detail один к одному (по полям id) На выходе нужно получить таблицу с полями main_id, f1, f2, f3, value1, value2, value3 где valueN это значение поля val из таблицы sub_detail для записи с param = N. Не все valueN могут присутствовать в базе. Базовый запрос выглядит так SELECT m.id AS main_id, m.f1, m.f2, m.f3, s1.val AS value1, s2.val AS value2, s3.val AS value3 FROM main m LEFT JOIN detail d ON ( d.main_id = m.id ) LEFT JOIN sub_detail s1 ON ( s1.id = d.id AND s1.param = 1 ) LEFT JOIN sub_detail s2 ON ( s2.id = d.id AND s2.param = 2 ) LEFT JOIN sub_detail s3 ON ( s3.id = d.id AND s3.param = 3 ) WHERE m.id = 1; Но он вместо одной записи возвращает столько записей, сколько есть дочерних записей в detail. Поставить группировку по main.id и вытаскивать MAX(s.val) я не могу, т.к. в запросе есть еще поля f1, f2 Есть вариант на основании detail и sub_detail построить view CREATE VIEW vw_detail ( main_id, param, val ) AS SELECT d.main_id, s.param, s.val FROM detail d JOIN sub_detail s ON ( d.id = s.id ) и вызывать ее в основном запросе SELECT m.id AS main_id, m.f1, m.f2, m.f3, s1.val AS value1, s2.val AS value2, s3.val AS value3 FROM main m LEFT JOIN vw_detail s1 ON ( s1.main_id = m.id AND s1.param = 1 ) LEFT JOIN vw_detail s2 ON ( s2.main_id = m.id AND s2.param = 2 ) LEFT JOIN vw_detail s3 ON ( s3.main_id = m.id AND s3.param = 3 ) WHERE m.id = 1; но в Interbase есть старый баг из-за которого при вызове LEFT JOIN view начинают игнорироваться индексы и происходит полное сканирование таблицы. А писать JOIN vw_detail нельзя, потому что некоторые (или даже все) параметры могут отсутствовать. Пробовал писать так CREATE VIEW vw_values ( main_id, value1, value2, value3 ) AS SELECT m.id, MAX(s1.val), MAX(s2.val), MAX(s3.val), FROM main m LEFT JOIN detail d ON ( d.main_id = m.id ) LEFT JOIN sub_detail s1 ON ( s1.id = d.id AND s1.param = 1 ) LEFT JOIN sub_detail s2 ON ( s2.id = d.id AND s2.param = 2 ) LEFT JOIN sub_detail s3 ON ( s3.id = d.id AND s3.param = 3 ) GROUP BY m.id; и потом в основном запросе SELECT m.id AS main_id, m.f1, m.f2, m.f3, v.value1, v.value2, v.value3 FROM main m JOIN vw_values v ON ( v.main_id = m.id ) WHERE m.id = 1; но тогда вначале идет построение VIEW с группировкой, а потом накладывание условия v.main_id = m.id. План получается дикий. Вариант заменить vw_values селективной процедурой тоже не подходит. Т.к. основной запрос выполняется в другой view, а синтаксис Interbase не позволяет внутри view использовать процедуры P.S. Вариант перейти на Firebird не предлагать Ссылка на DB Fiddle.
Ответы
Ответ 1
SELECT m.id AS main_id, m.f1, m.f2, m.f3, MAX(s1.val) AS value1, MAX(s2.val) AS value2, MAX(s3.val) AS value3 FROM main m LEFT JOIN detail d ON ( d.main_id = m.id ) LEFT JOIN sub_detail s1 ON ( s1.id = d.id AND s1.param = 1 ) LEFT JOIN sub_detail s2 ON ( s2.id = d.id AND s2.param = 2 ) LEFT JOIN sub_detail s3 ON ( s3.id = d.id AND s3.param = 3 ) WHERE m.id = 1 GROUP BY m.id, m.f1, m.f2, m.f3 fiddleОтвет 2
Денормализовал таблицу detail и скопировал туда поле param. После чего запрос переписался в такой вид SELECT m.id AS main_id, m.f1, m.f2, m.f3, s1.val AS value1, s2.val AS value2, s3.val AS value3 FROM main m LEFT JOIN detail d1 ON ( d1.main_id = m.id AND d1.param = 1 ) LEFT JOIN sub_detail s1 ON ( s1.id = d1.id ) LEFT JOIN detail d2 ON ( d2.main_id = m.id AND d2.param = 2 ) LEFT JOIN sub_detail s2 ON ( s2.id = d2.id ) LEFT JOIN detail d3 ON ( d3.main_id = m.id AND d3.param = 3 ) LEFT JOIN sub_detail s3 ON ( s3.id = d3.id ) WHERE m.id = 1; Если кто подскажет решение без денормализации - буду рад Update Причем поля detail.main_id и detail.param должны входить в общий индекс. Если завести два отдельных, то хоть и план строится с использованием индексов, но выполнение очень долгое
Комментариев нет:
Отправить комментарий