Вопрос или проблема
Я использую Preset.io, но полагаю, что функциональность довольно похожа для Superset и PowerBI. Думаю, это скорее вопрос данных.
Представьте следующую схему:
Люди
id | name | runtime_1 | runtime_2 | runtime_3 | ridetime_1 | ridetime_2 | ridetime_3 |
---|---|---|---|---|---|---|---|
1 | Джон | 12:00 | 12:31 | 12:23 | 28:03 | 28:16 | 27:56 |
2 | Джейн | null | null | null | 24:34 | 25:15 | 25:10 |
3 | Джим | null | null | null | null | null | null |
Интересы
id | interest |
---|---|
1 | Велоспорт |
2 | Бег |
Люди_Интересы
people_id | interest_id |
---|---|
1 | 1 |
1 | 2 |
2 | 2 |
Мой запрос выглядит следующим образом:
SELECT p.*,
LEAST(runtime_1, runtime_2, runtime_3) as fastest_runtime,
LEAST(ridetime_1, ridetime_2, ridetime3) as fastest_ridetime,
i.interest
FROM people AS p
LEFT JOIN people_interests AS pi ON p.id = pi.people_id
LEFT JOIN interests_i AS i ON pi.interests_id = i.id
Это, конечно, возвращает следующие результаты:
ПРИМЕЧАНИЕ: Я знаю, что LEAST
не вернёт результаты таким образом, но это скорее псевдокод для отображения примерных результатов.
id | name | runtime_1 | runtime_2 | runtime_3 | ridetime_1 | ridetime_2 | ridetime_3 | fastest_runtime | fastest_ridetime | interest |
---|---|---|---|---|---|---|---|---|---|---|
1 | Джон | 12:00 | 12:31 | 12:23 | 28:03 | 28:16 | 27:56 | 12:00 | 27:56 | Велоспорт |
1 | Джон | 12:00 | 12:31 | 12:23 | 28:03 | 28:16 | 27:56 | 12:00 | 27:56 | Бег |
2 | Джейн | null | null | null | 24:34 | 25:15 | 25:10 | null | 24:34 | Бег |
3 | Джим | null | null | null | null | null | null | null | null | null |
В Preset.io мне нужно сделать несколько вещей:
- Мне нужно использовать фильтры для
interest
, чтобы результаты их включали - Мне нужно вернуть
AVG
дляfastest_runtime
иfastest_ridetime
для всех людей
Проблема в том, что Джон
возвращается дважды (потому что у него два интереса). Следовательно, средние значения будут неверными. Если бы это была связь 1:M, у меня бы не было проблемы, но M:M создает проблему.
Как я могу это исправить?
Ответ или решение
Многие-ко-многие запросы в Preset.io/Superset: Решения и Подходы
При работе с реляционными базами данных и BI-инструментами, такими как Preset.io и Superset, нередко возникает проблема обработки данных с отношениями многие-ко-многим (M:M). В данном случае мы имеем три таблицы: People
, Interests
и People_Interests
, где одна и та же запись в таблице People
может относиться к нескольким записям в таблице Interests
, что создает дублирование данных при выполнении SQL-запросов. Решение данной проблемы требует понимания структуры данных и применения соответствующих агрегаций.
Более детальный анализ проблемы
На основе предоставленного примера, у нас есть следующая структура:
- Таблица People: содержит информацию о людях и их показателях времени.
- Таблица Interests: описывает интересы.
- Таблица People_Interests: связывает людей с их интересами.
Ваш первоначальный запрос:
SELECT p.*,
LEAST(runtime_1, runtime_2, runtime_3) AS fastest_runtime,
LEAST(ridetime_1, ridetime_2, ridetime_3) AS fastest_ridetime,
i.interest
FROM people AS p
LEFT JOIN people_interests AS pi ON p.id = pi.people_id
LEFT JOIN interests AS i ON pi.interest_id = i.id
Этот запрос корректно соединяет таблицы, но приводит к дублированию строк для John
, поскольку у него два интереса. Это затрудняет дальнейшую агрегацию данных, поскольку при вычислении средних значений получается искажение.
Решение проблемы дублирования
Чтобы избежать дублирования, необходимо использовать подзапросы или временные таблицы, чтобы сначала вычислить средние значения. Возможно, вам понадобится использовать GROUP BY
для агрегации на основе человека, а затем присоединить интересы. Ниже представлен пример, как это можно сделать:
WITH FastestTimes AS (
SELECT
p.id,
p.name,
LEAST(runtime_1, runtime_2, runtime_3) AS fastest_runtime,
LEAST(ridetime_1, ridetime_2, ridetime_3) AS fastest_ridetime
FROM
people AS p
)
SELECT
ft.*,
AVG(ft.fastest_runtime) OVER () AS avg_fastest_runtime,
AVG(ft.fastest_ridetime) OVER () AS avg_fastest_ridetime,
i.interest
FROM
FastestTimes AS ft
LEFT JOIN
people_interests AS pi ON ft.id = pi.people_id
LEFT JOIN
interests AS i ON pi.interest_id = i.id
Объяснение структуры запроса
-
CTE (Common Table Expression): Использование с Common Table Expression позволяет сначала извлечь значения
fastest_runtime
иfastest_ridetime
для каждого человека, что помогает избежать дублирования на этом этапе. -
Агрегация: Подсчет среднего значения выполняется с использованием оконной функции
AVG() OVER ()
. Это позволяет рассчитать средние значения по всем полученным интересам без дублирования по людям. -
Фильтрация интересов: После того как у вас есть список людей с их наилучшими значениями времени и интересами, вы можете применить необходимые фильтры к столбцу
interest
в интерфейсе Preset.io/Superset для визуализации данных.
Заключение
В решениях для работы с M:M связями в BI-инструментах, таких как Preset.io и Superset, ключевым моментом является правильная агрегация и группировка данных. Использование подзапросов или CTE помогает избежать дублирующихся результатов и обеспечивает точные расчеты для средних значений. С помощью представленного подхода вы сможете эффективно управлять данными и визуализировать результаты, сохраняя высокий уровень аналитики.