Заполнить пробелы в данных — UNION, PARTITION BY или JOIN?

Проблема

Есть пробелы в данных, которые необходимо заполнить. Хотел бы избежать UNION или PARTITION BY, если это возможно.

Запрос

Оператор select выглядит следующим образом:

SELECT
  count( r.incident_id ) AS incident_tally,
  r.severity_cd,
  r.incident_typ_cd
FROM
  report_vw r
GROUP BY
  r.severity_cd, r.incident_typ_cd
ORDER BY
  r.severity_cd,
  r.incident_typ_cd

Источники данных

Коды серьезности и коды типов инцидентов взяты из:

  • severity_vw
  • incident_type_vw

Столбцы:

  • инцидент_tally
  • серьезность_cd
  • инцидент_тип_cd

Фактические данные о результатах

36  0   ENVIRONMENT
1   1   DISASTER
27  1   ENVIRONMENT
4   2   SAFETY
1   3   SAFETY

Необходимые данные о результатах

36  0   ENVIRONMENT
0   0   DISASTER
0   0   SAFETY
27  1   ENVIRONMENT
0   1   DISASTER
0   1   SAFETY
0   2   ENVIRONMENT
0   2   DISASTER
4   2   SAFETY
0   3   ENVIRONMENT
0   3   DISASTER
1   3   SAFETY

Вопрос

Как бы вы использовали UNION, PARTITION BY или LEFT JOIN для подсчета нулей?


person Dave Jarvis    schedule 29.03.2010    source источник


Ответы (3)


Как насчет чего-то вроде

SELECT  COUNT(r.incident_id),
        crsjn.severity_cd,
        crsjn.incident_typ_cd
FROM    (
            SELECT  severity_cd,
                    incident_typ_cd
            FROM    severity_vw,
                    incident_type_vw
        ) crsjn LEFT JOIN
        report_vw r     ON  crsjn.severity_cd = r.severity_cd 
                        AND crsjn.incident_typ_cd = r.incident_typ_cd
GROUP BY crsjn.severity_cd,
        crsjn.incident_typ_cd
ORDER BY crsjn.severity_cd,
        crsjn.incident_typ_cd
person Adriaan Stander    schedule 29.03.2010

Проще всего было бы, если бы у вас был тип инцидента и серьезность инцидента в другой таблице.

SELECT COALESCE(sub.incident_tally), s.severity_cd, i.incident_type_cd
FROM incident_type i
CROSS JOIN incident_severity s
LEFT JOIN (
   ... your original sql statement...
) sub ON i.incident_typ_cd = sub.incident_type_cd
  AND s.severity_cd = sub.severity_cd

И если бы у вас не было этих таблиц, вы могли бы сделать что-то вроде этого. Я не понимаю, почему нет ограничения UNION. Но это работает.

CREATE TYPE VARCHAR_TABLE AS TABLE OF VARCHAR2(60);
CREATE TYPE NUMBER_TABLE AS TABLE OF NUMBER;

WITH inc AS (
  SELECT /*+ CARDINALITY(nt1 3) */ column_value AS incident_type_cd
  FROM TABLE(varchar_table('ENVIRONMENT', 'DISASTER', 'SAFETY')) nt1
),
sev AS (
  SELECT /*+ CARDINALITY(nt2 4) */ column_value AS severity_cd
  FROM TABLE(number_table(0,1,2,3)) nt2
) 
SELECT *
FROM inc
CROSS JOIN sev
LEFT JOIN (
   ... your original sql statement...
) sub ON i.incident_typ_cd = sub.incident_type_cd
  AND s.severity_cd = sub.severity_cd

4

person Scott Bailey    schedule 29.03.2010
comment
Для этого решения вы должны использовать подсказку кардинальности для вложенных таблиц. В противном случае оптимизатор будет считать, что каждая из этих таблиц имеет размер блока. - person Allan; 30.03.2010
comment
Спасибо за совет, Аллан. Я добавил подсказки к ответу. - person Scott Bailey; 30.03.2010

Единственный способ, который я могу придумать, - это еще больше нормализовать эту таблицу и использовать левое соединение.

person Fabian    schedule 29.03.2010
comment
Это не стол. Это представление данных, поступающих из нескольких таблиц. И нормализация больше невозможна — проект ушел в поддержку; разрешены только исправления ошибок и незначительные обновления. - person Dave Jarvis; 30.03.2010
comment
Затем я предлагаю получить весь список серьезностей и инцидентов из таблицы, а затем присоединиться к этому представлению с помощью левого соединения. - person Fabian; 30.03.2010