ВНИМАНИЕ: Это один БОЛЬШОЙ вопрос
У меня проблема с дизайном, которая началась просто, но на одном этапе роста меня полностью ошарашило.
У простой версии реальности есть хорошая плоская таблица фактов ...
Все имена были изменены, чтобы защитить невинных
CREATE TABLE raw_data (
tier0_id INT, tier1_id INT, tier2_id INT, tier3_id INT,
metric0 INT, metric1 INT, metric2 INT, metric3 INT
)
tierID относятся к объектам в дереве фиксированной глубины. Например, бизнес-иерархия.
Метрики - это просто показатели производительности, такие как количество пойманных лягушек или выпущенных голубей.
В отчете любезно пользователь выбирает что-то вроде следующего:
- 34 и 55 tier0_id - показаны отдельно
- все tier1_id - сгруппированы вместе
- все tier2_id - сгруппированы вместе
- все tier3_id - показаны отдельно
- метрики 2 и 3
Это дает мне следующий тип запроса:
SELECT
CASE WHEN @t0_grouping = 1 THEN NULL ELSE tier0_id END AS tier0_id,
CASE WHEN @t1_grouping = 1 THEN NULL ELSE tier1_id END AS tier1_id,
CASE WHEN @t2_grouping = 1 THEN NULL ELSE tier2_id END AS tier2_id,
CASE WHEN @t3_grouping = 1 THEN NULL ELSE tier3_id END AS tier3_id,
SUM(metric2) AS metric2, SUM(metric3) AS metric3
FROM
raw_data
INNER JOIN tier0_values ON tier0_values.id = raw_data.tier0_id OR tier0_values.id IS NULL
INNER JOIN tier1_values ON tier1_values.id = raw_data.tier1_id OR tier1_values.id IS NULL
INNER JOIN tier2_values ON tier2_values.id = raw_data.tier2_id OR tier2_values.id IS NULL
INNER JOIN tier3_values ON tier3_values.id = raw_data.tier3_id OR tier3_values.id IS NULL
GROUP BY
CASE WHEN @t0_grouping = 1 THEN NULL ELSE tier0_id END,
CASE WHEN @t1_grouping = 1 THEN NULL ELSE tier1_id END,
CASE WHEN @t2_grouping = 1 THEN NULL ELSE tier2_id END,
CASE WHEN @t3_grouping = 1 THEN NULL ELSE tier3_id END
Это хороший гибрид динамического SQL и параметризованных запросов. И да, я знаю, но SQL-CE заставляет людей делать странные вещи. Кроме того, это можно привести в порядок по мере внесения следующих изменений ...
Отныне нам нужно иметь возможность включать NULL в разные уровни. Это будет означать «относится ко ВСЕМ сущностям этого уровня».
Например, со следующими очень упрощенными данными:
Activity WorkingTime ActiveTime BusyTime
1 0m 10m 0m
2 0m 15m 0m
3 0m 20m 0m
NULL 60m 0m 45m
WorkingTime никогда не применяется к действию, поэтому все значения идут с NULL ID. Но ActiveTime конкретно относится к конкретному виду деятельности, поэтому он имеет законный идентификатор. BusyTime также против действия NULL, потому что это совокупность всех ActiveTime.
Если нужно отчитаться по этим данным, значения NULL - всегда - включаются в каждую строку, потому что NULL - означает - «относится ко всему». Данные будут выглядеть как ...
Activity WorkingTime ActiveTime BusyTime (BusyOnOtherActivities)
1 60m 10m 45m (45-10 = 35m)
2 60m 15m 45m (45-15 = 30m)
3 60m 20m 45m (45-20 = 25m)
1&2 60m 25m 45m (45-25 = 20m)
1&3 60m 30m 45m (45-30 = 15m)
2&3 60m 35m 45m (45-35 = 10m)
ALL 60m 45m 45m (45-45 = 0m)
Надеюсь, этот пример имеет смысл, потому что на самом деле это многоуровневая иерархия (согласно исходному примеру), и на каждом уровне допускаются значения NULL. Поэтому я попробую пример с 3 уровнями ...
t0_id | t1_id | t2_id | m1 | m2 | m3 | m4 | m5
1 3 10 | 0 10 0 0 0
1 4 10 | 0 15 0 0 0
1 5 10 | 0 20 0 0 0
1 NULL 10 | 60 0 45 0 0
2 3 10 | 0 5 0 0 0
2 5 10 | 0 10 0 0 0
2 6 10 | 0 15 0 0 0
2 NULL 10 | 50 0 30 0 0
1 3 11 | 0 7 0 0 0
1 4 11 | 0 8 0 0 0
1 5 11 | 0 9 0 0 0
1 NULL 11 | 30 0 24 0 0
2 3 11 | 0 8 0 0 0
2 5 11 | 0 10 0 0 0
2 6 11 | 0 12 0 0 0
2 NULL 11 | 40 0 30 0 0
NULL NULL 10 | 0 0 0 60 0
NULL NULL 11 | 0 0 0 60 0
NULL NULL NULL | 0 0 0 0 2
Это дало бы много-много разных возможных выходных записей в отчете, но вот несколько примеров ...
t0_id | t1_id | t2_id | m1 | m2 | m3 | m4 | m5
1 3 10 | 60 10 45 60 2
1 4 10 | 60 15 45 60 2
1 5 10 | 60 20 45 60 2
2 3 10 | 50 5 30 60 2
2 5 10 | 50 10 30 60 2
2 6 10 | 50 15 30 60 2
1 ALL 10 | 60 45 45 60 2
2 ALL 10 | 50 30 30 60 2
ALL 3 10 | 110 15 75 60 2
ALL 4 10 | 60 15 45 60 2
ALL 5 10 | 110 30 75 60 2
ALL 6 10 | 50 15 30 60 2
ALL 3 ALL | 180 30 129 120 2
ALL 4 ALL | 90 23 69 120 2
ALL 5 ALL | 180 49 129 120 2
ALL 6 ALL | 90 27 60 120 2
ALL ALL 10 | 110 129 129 60 2
ALL ALL 11 | 70 129 129 60 2
ALL ALL ALL | 180 129 129 120 2
1 3&4 ALL | 90 40 69 120 2
ALL 3&4 ALL | 180 53 129 120 2
Как бы беспорядочно это ни объяснялось, в моей голове есть полный и логичный смысл. Я понимаю, о чем спрашивают, но, судя по всему, я не могу написать запрос для этого, который не требует мучительных затрат времени.
Итак, как бы вы написали такой запрос и / или реорганизовали схему?
Я ценю, что люди будут спрашивать примеры того, что я сделал до сих пор, но я очень хочу сначала услышать неиспорченные идеи и советы других людей;)