Рекурсивный запрос с отношением hasMany - PullRequest
0 голосов
/ 08 февраля 2019

Представление таблицы представляет собой упрощение реальной проблемы, поскольку оно отражает ситуацию, но ее легче понять.

В двух таблицах содержатся определения единиц измерения.Существует таблица со всеми символами юнитов и таблица, связанная со многими, которая содержит определяющие сегменты всех юнитов.Например,

  • это определяет ярд как 0.9144 метр
  • это определяет цепь как 22 ярдов
  • это определяет фарлонг как 220ярды
  • он определяет акр как 1 время цепи 1 furlong

Таблицы:

measurement_units
id      name        is_base_unit
----------------------------------
1       meter       1
2       yard        0
3       chain       0
4       furlong     0
5       acre        0


measurement_unit_derivations
id      measurement_unit_id     derives_from_measurement_unit_id    factor
---------------------------------------------------------------------------
1       2                       1                                   0.9144
2       3                       2                                   22
3       4                       2                                   220
4       5                       3                                   1
5       5                       4                                   1

Я пытаюсь написать рекурсивную общую таблицувыражение, в котором соотношения для каждой единицы измерения рассчитываются относительно базовой единицы для соответствующего физического измерения.Результат должен, например, содержать отношение ярда к метру и отношение акра к м ^ 2.Это должно выглядеть так:

measurement_unit_id     name        ratio
-------------------------------------------
1                       meter       1
2                       yard        0.9144
3                       chain       20.1168
4                       furlong     201.168
5                       acre        4046.8564224

Проблема в том, что единица может быть получена из нескольких других единиц.Например, чтобы выяснить отношение одного акра к m ^ 2, мы увидим, что оно получено из одного фарлонга и одной цепочки, которые, в свою очередь, также получены из других единиц.

Iвыход за пределы:

WITH RECURSIVE cte AS (
    SELECT 
        id AS measurement_unit_id
        name,
        1 AS ratio
    FROM measurement_units
    WHERE is_base_unit = 1
    UNION ALL
    ???
)

Редактировать: Примеры таблиц с метрами и акрами удобны для описания, но также «слишком просты».Я создал еще один пример, в котором рекурсивный запрос также должен работать:

measurement_units
id      name        is_base_unit
----------------------------------
1       A           1
2       B           1
3       C           0
4       D           0
5       E           0

measurement_unit_derivations
id      measurement_unit_id     derives_from_measurement_unit_id    factor
---------------------------------------------------------------------------
1       3                       1                                   2
2       4                       2                                   2
3       4                       3                                   2
4       5                       4                                   2

Этот результат является целью:

measurement_unit_id     name        ratio
-------------------------------------------
1                       A           1
2                       B           1
3                       C           2
4                       D           8
5                       E           16

Вот быстрый и грязный SQL для их созданиядве таблицы и их содержание.

CREATE TABLE `measurement_units` (`id` int(10) UNSIGNED NOT NULL, `name` varchar(190) NOT NULL, `is_base_unit` tinyint(3) UNSIGNED NOT NULL);
CREATE TABLE `measurement_unit_derivations` (`id` int(10) UNSIGNED NOT NULL, `measurement_unit_id` int(10) UNSIGNED NOT NULL, `derived_from_measurement_unit_id` int(10) UNSIGNED NOT NULL, `factor` int(10) UNSIGNED NOT NULL);

INSERT INTO `measurement_units` (`id`, `name`, `is_base_unit`) VALUES (1, 'A', 1), (2, 'B', 1), (3, 'C', 0), (4, 'D', 0), (5, 'E', 0);
INSERT INTO `measurement_unit_derivations` (`id`, `measurement_unit_id`, `derived_from_measurement_unit_id`, `factor`) VALUES (1, 3, 1, 2), (2, 4, 2, 2), (3, 4, 3, 2), (4, 5, 4, 2);

1 Ответ

0 голосов
/ 08 февраля 2019

Вы правильно настроили рекурсивное семя.Мы начинаем с base_unit = 1.Следующий раздел после UNION ALL является рекурсивным термином.Здесь вы ссылаетесь на cte и присоединяете его к исходным таблицам, устанавливая отношения для итераций.

Ваш рекурсивный термин примет форму:

SELECT 
    mu.id,
    mu.name,
    cte.ratio * mud.factor
FROM
    cte
    INNER JOIN measurement_unit_derivations mud
        ON cte.measurement_unit_id = mud.derives_from_measurement_unit_id
    INNER JOIN measurement_units mud
        ON mud.measurement_unit_id = mu.id

Это создаст несколькозаписи для измерений, которые получены из двух или более измерений.Так как правило, как представляется, заключается в том, что мы умножаем несколько производных измерений вместе, чтобы определить соотношение (цепочки * furlows = акры), тогда мы можем агрегировать после того, как этот рекурсивный cte сделан.как есть сложение (SUM()).Таким образом, мы должны вычислить это.Я полагаю, что будет работать следующее:

SELECT measurement_unit_id, name, EXP(SUM(LOG(ratio))) FROM cte GROUP BY measurement_unit_id, name;

Соберите все это вместе:

WITH RECURSIVE cte AS (
    SELECT 
        measurement_unit_id
        name,
        1 AS ratio
    FROM measurement_units
    WHERE base_unit = 1
    UNION ALL
    SELECT 
        mu.id,
        mu.name,
        cte.ratio * mud.factor
    FROM
        cte
        INNER JOIN measurement_unit_derivations mud
            ON cte.measurement_unit_id = mud.derives_from_measurement_unit_id
        INNER JOIN measurement_units mud
            ON mud.measurement_unit_id = mu.id
)
SELECT measurement_unit_id, name, EXP(SUM(LOG(ratio))) FROM cte GROUP BY measurement_unit_id, name;
...