Уменьшить левое внешнее объединение в запросе SQL - PullRequest
1 голос
/ 22 марта 2020

Обновлено с помощью sqlfilld SQLfiddle

У меня есть запрос Oracle, где мне нужно уменьшить количество левого внешнего соединения для эффективной работы. Текущий запрос выполняется более 2 часов, и я хочу уменьшить его сложность, уменьшив количество операций объединения.

Без объединений запрос выполняется за 15 минут. Поэтому я хочу переписать логи c. Есть ли эффективный способ сделать это?

WITH myquery AS
(
    SELECT * 
    FROM TEST_FILE1
)
SELECT 
    A.Col3, A.Col1, A.Col2, A.Col4, A.Col5
  --  D.CB,
  --  NVL(D.CD, 0), NVL(D.CE, 0), NVL(D.EF, 0),
    ,CASE WHEN V1.Col1 IS NULL THEN 0 ELSE 1 END AS QQ1
    ,CASE WHEN V2.Col3 IS NULL THEN 0 ELSE 1 END AS QQ2
    ,CASE WHEN V3.Col1 IS NULL THEN 0 ELSE 1 END AS QQ3
    ,CASE WHEN V4.Col3 IS NULL THEN 0 ELSE 1 END AS QQ4
, case when V5.Col1 is NULL then 0 else 1 end as QQ5
, case when V6.Col3 is NULL then 0 else 1 end as QQ6
, case when V7.Col1 is NULL then 0 else 1 end as QQ7
, case when V8.Col3 is NULL then 0 else 1 end as QQ8
FROM (
  SELECT Col3, Col1, Col2, Col4, Col5 
  FROM (
    SELECT distinct Col3
    FROM myquery
  ) A1
  CROSS JOIN (
    SELECT distinct Col1
    FROM myquery
  ) A2
  CROSS JOIN (
    SELECT distinct Col2
    FROM myquery
  ) A3
  CROSS JOIN (
    SELECT distinct Col4
    FROM myquery
  ) A4
  CROSS JOIN (
    SELECT distinct Col5
    FROM myquery
  ) A5
  WHERE Col3 = 42
) A
LEFT JOIN myquery D on NVL(D.Col3, '-') = NVL(A.Col3, '-') AND NVL(D.Col1, '-') = NVL(A.Col1, '-') 
    AND NVL(D.Col2, '-') = NVL(A.Col2, '-') AND NVL(D.Col4, '-') = NVL(A.Col4, '-') AND NVL(D.Col5, 
'-') = NVL(A.Col5, '-')
LEFT JOIN (
  SELECT distinct Col1, Col3, Col5
  FROM myquery
) V1 on V1.Col1 = A.Col1 AND V1.Col3 = A.Col3 AND V1.Col5 = A.Col5
LEFT JOIN (
  SELECT distinct Col3, Col5, Col2
  FROM myquery
) V2 on V2.Col3 = A.Col3 AND V2.Col5 = A.Col5 AND V2.Col2 = A.Col2
LEFT JOIN (
  SELECT distinct Col3, Col5, Col1, Col2
  FROM myquery
) V3 on V3.Col3 = A.Col3 AND V3.Col5 = A.Col5 AND V3.Col1 = A.Col1 AND V3.Col2 = A.Col2
LEFT JOIN (
  SELECT distinct Col3, Col5, Col2
  FROM myquery
  WHERE Col1 in ('Bert','Myra')
) V4 on V4.Col3 = A.Col3 AND V4.Col5 = A.Col5 AND V4.Col2 = A.Col2
LEFT JOIN (
  SELECT distinct Col1, Col3
  FROM myquery
) V5 on V5.Col1 = A.Col1 AND V5.Col3 = A.Col3
LEFT JOIN (
  SELECT distinct Col3, Col2
  FROM myquery
) V6 on V6.Col3 = A.Col3 AND V6.Col2 = A.Col2
LEFT JOIN (
  SELECT distinct Col3, Col1, Col2
  FROM myquery
) V7 on V7.Col3 = A.Col3 AND V7.Col1 = A.Col1 AND V7.Col2 = A.Col2
LEFT JOIN (
  SELECT distinct Col3, Col2
  FROM myquery
  WHERE Col1 in ('Bert','Myra')
) V8 on V8.Col3 = A.Col3 AND V8.Col2 = A.Col2

До сих пор я думал об использовании аналитической оконной функции, но не получил желаемого результата. Любые выводы будут высоко оценены.

Вот мои входные данные таблицы test_file

+------+------+------+------+------+
| COL1 | COL2 | COL3 | COL4 | COL5 |
+------+------+------+------+------+
| Bert | "M"  | 42   | 68   | 166  |
| Carl | "M"  | 32   | 70   | 155  |
| Dave | "M"  | 39   | 72   | 167  |
| Elly | "F"  | 30   | 66   | 124  |
| Fran | "F"  | 33   | 66   | 115  |
| Hank | "M"  | 30   | 71   | 158  |
| Jake | "M"  | 32   | 69   | 143  |
| Luke | "M"  | 34   | 72   | 163  |
| Neil | "M"  | 36   | 75   | 160  |
| Page | "F"  | 31   | 67   | 135  |
| Alex | "M"  | 41   | 74   | 170  |
| Gwen | "F"  | 26   | 64   | 121  |
| Ivan | "M"  | 53   | 72   | 175  |
| Kate | "F"  | 47   | 69   | 139  |
| Myra | "F"  | 23   | 62   | 98   |
| Omar | "M"  | 38   | 70   | 145  |
| Quin | "M"  | 29   | 71   | 176  |
| Ruth | "F"  | 28   | 65   | 131  |
+------+------+------+------+------+

Из этой таблицы я хочу создать каждую возможную комбинацию, взяв различные значения каждого столбца, применяя перекрестное соединение. Это даст 7776 записей с моим фильтром на col1=42. потому что я хочу все возможные комбинации только для этого столбца.

С помощью этой комбинации я хочу проверить, какие все комбинации столбцов ios равны нулю, используя множество комбинаций левых внешних объединений.

Вывод (частичный):

+------+------+------+------+------+-----+-----+-----+-----+-----+-----+-----+-----+
| COL3 | COL1 | COL2 | COL4 | COL5 | QQ1 | QQ2 | QQ3 | QQ4 | QQ5 | QQ6 | QQ7 | QQ8 |
+------+------+------+------+------+-----+-----+-----+-----+-----+-----+-----+-----+
| 42   | Page | "F"  | 68   | 176  | 0   | 0   | 0   | 0   | 0   | 0   | 0   | 0   |
| 42   | Alex | "F"  | 62   | 143  | 0   | 0   | 0   | 0   | 0   | 0   | 0   | 0   |
| 42   | Fran | "M"  | 66   | 175  | 0   | 0   | 0   | 0   | 0   | 1   | 0   | 1   |
| 42   | Omar | "F"  | 70   | 176  | 0   | 0   | 0   | 0   | 0   | 0   | 0   | 0   |
| 42   | Elly | "M"  | 72   | 124  | 0   | 0   | 0   | 0   | 0   | 1   | 0   | 1   |
| 42   | Quin | "M"  | 64   | 160  | 0   | 0   | 0   | 0   | 0   | 1   | 0   | 1   |
| 42   | Omar | "M"  | 64   | 158  | 0   | 0   | 0   | 0   | 0   | 1   | 0   | 1   |
| 42   | Kate | "F"  | 62   | 176  | 0   | 0   | 0   | 0   | 0   | 0   | 0   | 0   |
| 42   | Neil | "F"  | 69   | 145  | 0   | 0   | 0   | 0   | 0   | 0   | 0   | 0   |
| 42   | Dave | "F"  | 62   | 163  | 0   | 0   | 0   | 0   | 0   | 0   | 0   | 0   |
| 42   | Ruth | "M"  | 70   | 115  | 0   | 0   | 0   | 0   | 0   | 1   | 0   | 1   |
| 42   | Bert | "M"  | 65   | 121  | 0   | 0   | 0   | 0   | 1   | 1   | 1   | 1   |
| 42   | Bert | "M"  | 72   | 145  | 0   | 0   | 0   | 0   | 1   | 1   | 1   | 1   |
| 42   | Omar | "M"  | 62   | 158  | 0   | 0   | 0   | 0   | 0   | 1   | 0   | 1   |
| 42   | Ruth | "M"  | 75   | 131  | 0   | 0   | 0   | 0   | 0   | 1   | 0   | 1   |
+------+------+------+------+------+-----+-----+-----+-----+-----+-----+-----+-----+

1 Ответ

1 голос
/ 22 марта 2020

При проверке наличия данных в таблице мы используем EXISTS или IN, а не JOIN (SELECT DISTINCT ...). Следовательно, это вопрос, который я, вероятно, придумаю:

WITH myquery AS
(
  SELECT * FROM TEST_FILE1
)
, a as
(
  select col1, col2, 42 as col3, col4, col5
  from
  (
    (select distinct col1 from myquery)
      cross join
    (select distinct col2 from myquery)
      cross join
    (select distinct col4 from myquery)
      cross join
    (select distinct col5 from myquery)
  )
)
select
  a.col1, a.col2, a.col3, a.col4, a.col5,
  case when (col1, col3, col5)       in (select col1, col3, col5       from myquery                               ) then 1 else 0 end as v1,
  case when (col2, col3, col5)       in (select col2, col3, col5       from myquery                               ) then 1 else 0 end as v2,
  case when (col1, col2, col3, col5) in (select col1, col2, col3, col5 from myquery                               ) then 1 else 0 end as v3,
  case when (col2, col3, col5)       in (select col2, col3, col5       from myquery where col1 in ('Bert', 'Myra')) then 1 else 0 end as v4,
  case when (col1, col3)             in (select col1, col3             from myquery                               ) then 1 else 0 end as v5,
  case when (col2, col3)             in (select col2, col3             from myquery                               ) then 1 else 0 end as v6,
  case when (col1, col2, col3)       in (select col1, col2, col3       from myquery                               ) then 1 else 0 end as v7,
  case when (col2, col3)             in (select col2, col3             from myquery where col1 in ('Bert', 'Myra')) then 1 else 0 end as v8
from a
order by a.col1, a.col2, a.col3, a.col4, a.col5;

Если ваш реальный запрос здесь: WITH myquery AS (...) - это больше, чем просто SELECT * FROM TEST_FILE1, вы можете использовать /*+MATERIALIZE*/ Подсказка, чтобы ускорить доступ к нему.

...