Почему вызов двух предложений WITH, один из которых вызывает другой, не работает? - PullRequest
1 голос
/ 28 марта 2019

Простая практическая проблема: у меня есть таблица, const_a, которая содержит огромное количество строк и столбцов.Когда я вызываю SELECT A, B, C FROM const_a WHERE restrictive_conditions, я получаю все строки, над которыми я хочу работать, и единственные столбцы, которые мне нужны.

Строки сгруппированы по столбцу A, значения находятся в столбце C, иварианты в колонке B.Моя цель - получить все значения C из всех групп A, у которых есть хотя бы одна строка, в которой B имеет определенное значение, скажем, 'v'.

Первый запрос будет:

SELECT C FROM const_a
WHERE restrictive_conditions
AND A IN (SELECT DISTINCT A FROM const_a WHERE B LIKE 'v')

B LIKE 'v' не является слишком строгим в отношении const_a, и я не хочу повторять restrictive_conditions в SELECT DISTINCT, поэтому я буду использовать предложение WITH, напримерthis:

WITH tmp_a AS (SELECT A, B, C FROM const_a WHERE restrictive_conditions)
SELECT C FROM tmp_a
WHERE A IN (SELECT DISTINCT A FROM tmp_a WHERE B LIKE 'v')

Этот запрос работает в разумные сроки и возвращает ожидаемые значения.

Последний SELECT DISTINCT довольно неясен в контексте запроса, поэтому для простотыпонимания, я добавляю вторую WITH:

WITH tmp_a AS (SELECT A, B, C, FROM const_a WHERE restrictive_conditions),
tmp_b AS (SELECT DISTINCT A FROM tmp_a WHERE B LIKE 'v')
SELECT C FROM tmp_a WHERE A IN (SELECT A FROM tmp_b) ;

Я даю tmp_a и tmp_b значимых имен, здесь это будет похоже на minimal_range и valid_groups, и называю этоВ тот день, когда я пытаюсь выполнить этот запрос, у меня постоянно появляются ошибки отключения, которые, согласно моим поискам, указывают на то, что Oracle в фоновом режиме зависал сам по себе, не сообщая мне.

Я попробовал и протестировал все способы наложения псевдонимовиспользовал INNER JOIN вместо WHERE ... IN ..., но не повезло;когда у меня есть два или более предложений WITH, одно вызывает другое, и я вызываю их вместе, происходит сбой.В зависимости от того, как я его построил, я также получал пустые результаты (хотя попытки разных частей независимо давали ожидаемые результаты, как будто предложение WITH очищалось после вызова в другом).Это нежелательный результат.

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

Может кто-нибудь объяснить, почему это не работает?Я надеюсь, что это простая синтаксическая ошибка, но ни консоль, ни клиент-разработчик Oracle SQL не дают таких указаний, только потеря соединенияУ кого-нибудь есть решение?

Это делается с помощью Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 в соответствии с командой sqlplus.Я набрал все это, поэтому ошибка не из-за простой опечатки, но спасибо за любое исправление.

Редактировать: я должен указать, я также добавил /*+ MATERIALIZE */ после SELECT в моем WITH пункты, и это ничего не изменило.

Правка 2: Неважно, я думал, что нашел опустошенное предложение.

Правка 3: Привет всем, я нашел решение, илискорее я должен сказать, что нашел проблему.У меня было более одного столбца 'C', и один из них был столбцом CLOB.Мне показалось странным, что код MatBailie работал, когда я его набирал, но не мой, поэтому я действительно попытался создать свой собственный запрос, изменив его постепенно, и в тот момент, когда я запросил дополнительные столбцы значений, он перестал работать и попытался добавитьодин или другой показали, что мой столбец CLOB (и я проверял то же самое с BLOB) был слишком большим.Мне придется использовать другой запрос.

Поскольку MatBailie был прав, я приму его решение.Спасибо APC за вашу помощь, хотя я не смог попробовать.У меня такое чувство, что след подсказал бы правильное решение.

1 Ответ

2 голосов
/ 28 марта 2019

Ваш подход должен работать как написано, см. Этот тест:


Я думаю, однако, что вы можете немного упростить вычисления, используя аналитические функции вместо IN() ...

WITH
  restrict_and_check AS
(
  SELECT
    A, B, C,
    MAX(CASE WHEN B = 'v' THEN 1 ELSE 0 END)
      OVER (
        PARTITION BY A
      )
        AS a_includes_b_that_equals_v
  FROM
    const_a
  WHERE
    restrictive_condition
)
SELECT
  C
FROM
  restrict_and_check
WHERE
  a_includes_b_that_equals_v = 1

MAX() OVER () подобен подзапросу и с агрегатной функцией.

В приведенном выше примере он разделяет данные по столбцу A и передает функции все строки с одинаковым значением.

Функция в этом случае - MAX(CASE WHEN B = 'v' THEN 1 ELSE 0 END), которая возвращает 1, если какая-либо из входящих строк имеет B = 'v', в противном случае она возвращает 0.

Это немного похоже на ...

SELECT
  *
FROM
  a_data_set
INNER JOIN
(
  SELECT
    A,
    MAX(CASE WHEN B = 'v' THEN 1 ELSE 0 END) AS a_includes_b_that_equals_v
  FROM
    a_data_set
  GROUP BY
    A
)
  a_check
    ON a_check.A = a_data_set.A

Для более ясного объяснения ищите Аналитические функции или Оконные функции.

...