Объедините несколько строк в один столбец, используя функцию в Oracle с 3 связанными таблицами - PullRequest
0 голосов
/ 07 декабря 2010

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

table_a:

a_id    a_name
1       aaa
2       bbb
3       ccc

table_b:

b_id    b_name
1       ddd
2       eee
3       fff

table_c

a_id    b_id
1       2
1       3
2       1
3       1
3       2
3       3

У меня есть таблицы выше, и мне нужен оператор SELECT, который выполняет это:

a_name  list_of_b_name
aaa     eee,fff
bbb     ddd
ccc     ddd,eee,fff

Возможно, есть некоторые функции Oracle, которые я могу использоватьчтобы упростить процесс, как WM_CONCAT (еще не пробовал), но мне нужно использовать FUNCTION, и это моя попытка после просмотра статьи, указанной выше:

CREATE OR REPLACE
FUNCTION f_test(id IN table_c.a_id % TYPE) RETURN VARCHAR2 IS
  l_text  VARCHAR2(32767) := NULL;
BEGIN
  FOR cur_rec IN (SELECT b_id FROM table_c WHERE a_id = id) LOOP
    l_text := l_text || ',' || cur_rec.b_id;
  END LOOP;
  RETURN LTRIM(l_text, ',');
END;

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

SELECT a_id, f_test(a_id)
FROM table_c
GROUP BY a_id;

Как и следовало ожидать из приведенного выше кода (не то, что я хочу, это то, что я получаю):

a_id    list_of_b_id
1       2,3
2       1
3       1,2,3

Я пробовал много вещей как с помощью SELECT, так иКод функции, но я не могу делать то, что мне нужно ...

Ответы [ 3 ]

1 голос
/ 07 декабря 2010

измените SQL-запрос, чтобы получить список bnames вместо b_ids

с

SELECT b_id FROM table_c WHERE a_id = id

на

SELECT b_name 
  FROM table_b b, table_c c 
   WHERE b.b_id = c.c_id
     and b.a_id = id

Вместо получения идентификаторов из таблицы bвместо этого вы должны получать имена.

1 голос
/ 07 декабря 2010

Вам нужно изменить свою функцию так, чтобы она напоминала:

CREATE OR REPLACE FUNCTION f_test(IN_ID IN TABLE_C.a_id%TYPE) 
RETURN VARCHAR2 
IS

  l_text  VARCHAR2(32767) := NULL;

BEGIN

  FOR cur_rec IN (SELECT b.b_name 
                    FROM TABLE_B b
                    JOIN TABLE_C c ON c.b_id = b.b_id
                   WHERE c.a_id = IN_ID) LOOP
    l_text := l_text || ',' || cur_rec.b_id;
  END LOOP;

  RETURN LTRIM(l_text, ',');

END;

Затем вы можете использовать:

SELECT a.a_name, 
       f_test(a.a_id)
  FROM TABLE_A a
0 голосов
/ 07 декабря 2010

Во-первых, как указывает @OMGPonies, вам нужно переписать функцию, чтобы она извлекала необходимые данные из TABLE_B.

SQL> create or replace function f_test
  2      (p_id in table_c.a_id%type)
  3      return varchar2
  4  is
  5      l_text  varchar2(32767) := null;
  6  begin
  7      for cur_rec in (select b_name
  8                      from table_c c
  9                              join table_b b
 10                                  on (b.b_id = c.b_id)
 11                      where c.a_id = p_id)
 12      loop
 13          l_text := l_text || ',' || cur_rec.b_name;
 14      end loop;
 15      return ltrim(l_text, ',');
 16  end;
 17  /

Function created.

SQL>

Аналогично вам нужно присоединиться к TABLE_A в запросе:

SQL> select a.a_name
  2          , f_test(c.a_id)
  3  from table_c c
  4          join table_a a
  5              on (a.a_id = c.a_id)
  6  group by a.a_name
  7  /
        , f_test(c.a_id)
                 *
ERROR at line 2:
ORA-00979: not a GROUP BY expression


SQL>

О, это был сюрприз. Дело в том, что F_TEST не является агрегирующей функцией, поэтому GROUP BY здесь не работает, если мы не включим эту функцию:

SQL> select a.a_name
  2          , f_test(c.a_id) 
  3  from table_c c
  4          join table_a a
  5              on (a.a_id = c.a_id)
  6  group by a.a_name, f_test(c.a_id)
  7  /

A_N F_TEST(C.A_ID)
--- ---------------------------------------------
bbb ddd
ccc ddd,eee,fff
aaa eee,fff

SQL>

Кстати, без предложения GROUP BY мы получили бы шесть строк (по одной на каждую строку в TABLE_C). Которого мы не хотим.

Проблема с решением связана с производительностью: функция выполняется один раз для каждой строки в TABLE_C. Этот цикл в TABLE_B стал бы немного дороже, если бы любая из таблиц была большой. Агрегирующий раствор более эффективен:

SQL> select a.a_name
  2          , wm_concat(b.b_name) 
  3  from table_c c
  4          join table_b b
  5              on (b.b_id = c.b_id)
  6          join table_a a
  7              on (a.a_id = c.a_id)
  8  group by a.a_name
  9  /

A_N WM_CONCAT(B.B_NAME)
--- ---------------------------------------------
aaa fff,eee
bbb ddd
ccc fff,eee,ddd

SQL> 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...