Коррелированный подзапрос Oracle SQL для получения ровно одной строки

В коррелированном подзапросе Oracle SQL

Мне нужно, чтобы в подзапросе было условие для получения ровно одной строки (если строк больше, он не должен получать никаких строк).

И приведенный ниже запрос работает нормально. Но для этого нужно дважды запрашивать одну и ту же таблицу.

SELECT m.pk_1
      ,m.pk_2
      ,m.fld_3
      ,(SELECT c.pk_1
        FROM   child_tab c
        WHERE  (SELECT COUNT(*)
                FROM   child_tab c2
                WHERE  c2.pk_1 = m.pk_1
                AND    rownum <= 2) = 1
        AND    c.pk_1 = m.pk_1) c_pk_1
FROM   master_tab m
WHERE  m.pk_1 = '018'
AND    m.pk_2 = 'value'

Изменив подзапрос с использованием аналитической функции, приведенный ниже запрос дает ошибку

SELECT m.pk_1
      ,m.pk_2
      ,m.fld_3
      ,(SELECT ch.pk_1
        FROM   (SELECT COUNT(*) over() cnt
                      ,c.pk_1
                FROM   child_tab c
                WHERE  c.pk_1 = m.pk_1
                AND    rownum <= 2) ch
        WHERE  cnt = 1
        AND    c.pk_1 = m.pk_1) c_pk_1
FROM   master_tab m
WHERE  m.pk_1 = '018'
AND    m.pk_2 = 'value'

ORA-00904 m.pk_1 is an invalid identifier

Есть ли лучший способ сделать это с помощью Oracle SQL?

ОБНОВЛЕНИЕ

Мое реальное требование состоит в том, чтобы я генерировал этот SQL как процесс рекурсии, который ведет к многим подобным уровням. И выбранные мной данные не будут ограничиваться ключом. И мне нужно только решение SQL, поэтому PLSQL - это не выбор.


person Stalin Gino    schedule 03.04.2013    source источник


Ответы (2)


Учти это. У меня нет возможности выполнить и проверить это. Так что, пожалуйста, дайте мне знать о любых проблемах

   SELECT m.pk_1, m.pk_2, m.fld_3, C.PK1
   FROM MASTER_TAB M, CHILD_TAB C
   WHERE c.pk_1 = m.pk_1 AND m.pk_1 = '018'
   AND    m.pk_2 = 'value'
   AND EXISTS
   (
   SELECT *
   FROM CHILD_TAB C2
   c2.pk_1 = m.pk_1
   HAVING COUNT(*) = 1
   )

Отредактировано:

   SELECT m.pk_1, m.pk_2, m.fld_3, C.PK1
   FROM MASTER_TAB M, (
   SELECT c2.pk_1
   FROM CHILD_TAB C2
   HAVING COUNT(*) = 1
   ) C
   WHERE c.pk_1 = m.pk_1 AND m.pk_1 = '018'
   AND    m.pk_2 = 'value'
person Santhosh    schedule 03.04.2013
comment
Этот запрос снова выполняет два выбора в дочерней таблице, и он не будет работать, как вы делаете SELECT * в то время как HAVING count(*), что ограничивает выбор GROUP BY столбцами. Если не использовать «группировать по», вы не выберете ничего. - person Stalin Gino; 03.04.2013
comment
Это тоже не сработает. SELECT c2.pk_1 FROM CHILD_TAB C2 HAVING COUNT(*) = 1 c2.pk_1 не может быть выбран. Пожалуйста, прочтите в группе, имея - person Stalin Gino; 03.04.2013

Oracle не может поместить значение m.pk_1 в подзапрос на два уровня ниже. Однако, учитывая, что вы указываете значение для m.pk_1 в предложении where верхнего уровня, вы можете просто поместить его прямо в свой скалярный подзапрос следующим образом:

SELECT m.pk_1
      ,m.pk_2
      ,m.fld_3
      ,(SELECT ch.pk_1
        FROM   (SELECT COUNT(*) over() cnt
                      ,c.pk_1
                FROM   child_tab c
                WHERE  c.pk_1 = '018'
                AND    rownum <= 2) ch
        WHERE  cnt = 1
        AND    c.pk_1 = m.pk_1) c_pk_1
FROM   master_tab m
WHERE  m.pk_1 = '018'
AND    m.pk_2 = 'value';

В качестве альтернативы, учитывая, что m.pk_1 = c.pk_1, вам не нужно выбирать c.pk_1, и вы можете просто проверить, существуют ли строки, используя оператор case, возвращая m.pk_1, если есть только одна строка:

SELECT m.pk_1
      ,m.pk_2
      ,m.fld_3
      ,CASE WHEN (SELECT COUNT(*)
                  FROM   child_tab c
                  WHERE  c.pk_1 = m.pk_1
                  AND    rownum <= 2) = 1 THEN
          m.pk_1
       END c_pk_1
FROM   master_tab m
WHERE  m.pk_1 = '018'
AND    m.pk_2 = 'value';

Наконец, вы всегда можете создать функцию PL / SQL, которая будет выполнять поиск за вас, возвращая null, когда есть too_many_rows. Если у вас небольшое количество разных значений для m.pk_1 и вы используете 11g, это может быть функция кеширования результатов, которая должна работать хорошо:

CREATE FUNCTION get_id(m_pk master_tab.pk_1%TYPE) RETURN child_tab.pk_1%TYPE AS
  retval child_tab.pk_1%TYPE;
BEGIN

  SELECT t.pk_1
  INTO   retval
  FROM   child_tab t
  WHERE  t.pk_1 = m_pk
  AND    ROWNUM <= 2;

  RETURN retval;

EXCEPTION
  WHEN TOO_MANY_ROWS THEN
    RETURN NULL;
END;

SELECT m.pk_1
      ,m.pk_2
      ,m.fld_3
      ,get_id(m.pk_1) c_pk_1
FROM   master_tab m
WHERE  m.pk_1 = '018'
AND    m.pk_2 = 'value';
person Chris Saxon    schedule 03.04.2013
comment
Спасибо за попытку. Мое реальное требование состоит в том, чтобы я генерировал этот SQL как процесс рекурсии, который ведет к многим подобным уровням. Поэтому использование вашего первого подхода с жестко запрограммированным предложением where не сработает. И данные, которые я выбираю, не будут ограничены ключом, поэтому второй подход также не сработает для меня. И мне нужно только решение SQL, поэтому PLSQL - это не выбор. - person Stalin Gino; 04.04.2013
comment
Но эти решения хорошо подходят для заданного мной вопроса. Спасибо. - person Stalin Gino; 04.04.2013
comment
Я не совсем уверен, чего вы пытаетесь достичь. Я думаю, вам нужно создать новый вопрос, описывающий, что вам нужно для объяснения бизнес-требований. - person Chris Saxon; 04.04.2013