Собственный запрос Oracle для получения структуры отношений один ко многим - PullRequest
0 голосов
/ 23 февраля 2012

Может ли кто-нибудь помочь мне с запросом на выборку отношения один-ко-многим в oracle

Структура таблицы:

Table: STUD_NAME                       Table:CLASS                  

STUD_No    STUD_Name                     Class_ID       STUD_No        CLASS_NAME
-------------------                    -------------------------------------------     
1         Sam                            1                1             PHYSICS
2         Michael                        2                1             MATHEMATICS 
3         Patrick                        3                2             PHYSICS
5         Leena                          4                2             CHEMISTRY

Я попытался использовать концепции соединения, поэтому он вернул дубликатстроки в таблице STUD_NAME, такие как

  1         Sam  PHYSICS
  1         Sam  CHEMISTRY

Мне нужен вывод, например,

1 Sam  PHYSICS,MATHEMATICS
2 Michael  PHYSICS,CHEMISTRY

Ответы [ 5 ]

1 голос
/ 23 февраля 2012

Вы должны быть в состоянии сделать это, используя функцию SYS_CONNECT_BY_PATH.Для вашего примера должно работать что-то вроде следующего:

select
   stud_no,
   stud_name,
   substr(SYS_CONNECT_BY_PATH(class_name, ', '),2) classes
from
   (
   select     
     cn.stud_no,
     sn.stud_name,
     cn.class_name,
     count(*) OVER ( partition by cn.stud_no ) cnt,
     ROW_NUMBER () OVER ( partition by cn.stud_no order by cn.class_name) seq
   from
     class_name cn
    ,stud_name sn
   where
     sn.stud_no = cn.stud_no
   group by cn.stud_no, sn.stud_name, cn.class_name)
where
   seq=cnt
start with
   seq=1
connect by prior
   seq+1=seq
and prior
   stud_no=stud_no; 

Чтобы разбить это на части ...

Внутренний запрос

   select     
     sn.stud_no,
     sn.stud_name,
     cn.class_name,
     count(*) OVER ( partition by cn.stud_no ) cnt,
     ROW_NUMBER () OVER ( partition by cn.stud_no order by cn.class_name) seq
   from
     class_name cn
    ,stud_name sn
   where
     sn.stud_no = cn.stud_no
   group by cn.stud_no, sn.stud_name, cn.class_name

даст набор результатовнапример:

    STUD_NO  STUD_NAME  CLASS_NAME  CNT  SEQ
    001      SAM        CHEMISTRY   2    1
    001      SAM        PHYSICS     2    2
    002      MICHAEL    ART         3    1
    002      MICHAEL    HISTORY     3    2
    002      MICHAEL    PHYSICS     3    3

Обратите внимание, что набор результатов упорядочен таким образом, что записи классов для каждого учащегося сгруппированы в соответствии с stud_no.CNT указывает общее количество записей в группе (разделе), к которой принадлежит запись (включая общее количество всех записей с одинаковым stud_no), а SEQ указывает уникальную последовательность / ранг в этой группе (в данном случае на основев алфавитном порядке по имени класса).

Затем оставшаяся часть запроса строит список имен классов, разделенных запятыми, для каждого учащегося путем обхода результирующего набора с использованием значений CNT и SEQ следующим образом:

substr(SYS_CONNECT_BY_PATH(class_name, ', '),2) classes
-- builds a list of class names separated by a comma 
-- (the substr function is just there to remove the first delimiter)

where seq=cnt -- this condition indicates the last record in each group/list

start with seq=1 
-- the starting point for each group (i.e. start a new list every time a seq 
-- value of 1 is encountered while traversing the result set)

connect by prior seq+1=seq and prior stud_no=stud_no 
-- defines the connection between one list element and the next; the next 
-- element in a list will have the same stud_no as the prior element AND a 
-- seq equal to the prior element's seq +1 

Результат:

    STUD_NO  STUD_NAME  CLASSES
    001      SAM        CHEMISTRY, PHYSICS
    002      MICHAEL    ART, HISTORY, PHYSICS

Этот подход и несколько других возможных вариантов описаны здесь: http://www.dba -oracle.com / t_converting_rows_columns.htm

Надеюсь, это поможет!

1 голос
/ 23 февраля 2012

Для 9i использования:

select   S.STUD_NO, STUD_NAME, WM_CONCAT(CLASS_NAME)
from     STUD_NAME S, CLASS C
where    S.STUD_NO = C.STUD_NO
group by S.STUD_NO, STUD_NAME

Для 11 г и более вы можете использовать listagg вместо wm_concat. Смотрите здесь для хорошего списка методов агрегирования строк.

1 голос
/ 23 февраля 2012
CREATE OR REPLACE TYPE t_string_agg AS OBJECT
(
  g_string  VARCHAR2(32767),

  STATIC FUNCTION ODCIAggregateInitialize(sctx  IN OUT  t_string_agg)
    RETURN NUMBER,

  MEMBER FUNCTION ODCIAggregateIterate(self   IN OUT  t_string_agg,
                                       value  IN      VARCHAR2 )
     RETURN NUMBER,

  MEMBER FUNCTION ODCIAggregateTerminate(self         IN   t_string_agg,
                                         returnValue  OUT  VARCHAR2,
                                         flags        IN   NUMBER)
    RETURN NUMBER,

  MEMBER FUNCTION ODCIAggregateMerge(self  IN OUT  t_string_agg,
                                     ctx2  IN      t_string_agg)
    RETURN NUMBER
);
/
SHOW ERRORS


CREATE OR REPLACE TYPE BODY t_string_agg IS
  STATIC FUNCTION ODCIAggregateInitialize(sctx  IN OUT  t_string_agg)
    RETURN NUMBER IS
  BEGIN
    sctx := t_string_agg(NULL);
    RETURN ODCIConst.Success;
  END;

  MEMBER FUNCTION ODCIAggregateIterate(self   IN OUT  t_string_agg,
                                       value  IN      VARCHAR2 )
    RETURN NUMBER IS
  BEGIN
    SELF.g_string := self.g_string || ',' || value;
    RETURN ODCIConst.Success;
  END;

  MEMBER FUNCTION ODCIAggregateTerminate(self         IN   t_string_agg,
                                         returnValue  OUT  VARCHAR2,
                                         flags        IN   NUMBER)
    RETURN NUMBER IS
  BEGIN
    returnValue := RTRIM(LTRIM(SELF.g_string, ','), ',');
    RETURN ODCIConst.Success;
  END;

  MEMBER FUNCTION ODCIAggregateMerge(self  IN OUT  t_string_agg,
                                     ctx2  IN      t_string_agg)
    RETURN NUMBER IS
  BEGIN
    SELF.g_string := SELF.g_string || ',' || ctx2.g_string;
    RETURN ODCIConst.Success;
  END;
END;
/
SHOW ERRORS


CREATE OR REPLACE FUNCTION string_agg (p_input VARCHAR2)
RETURN VARCHAR2
PARALLEL_ENABLE AGGREGATE USING t_string_agg;
/
SHOW ERRORS

Затем выполните

select stud_name, string_agg(class_name)
from stud_name s, class c
where s.stud_no = c.stud_no
group by stud_name

Реквизит asktom.oracle.com

0 голосов
/ 23 февраля 2012

Хорошо, в SQLite мне удалось добиться ...

sqlite> select stud_name, group_concat(class_name)
   ...> from stud_name s, class c
   ...> where s.stud_no = c.stud_no
   ...> group by stud_name
   ...> ;
Mike|Physics
Sam|Math,Physics

В Oracle 9i, я полагаю, вы можете просто заменить group_concat сверху на wm_concat.

Дайте мне знать!

0 голосов
/ 23 февраля 2012

Вопрос отформатирован плохо, но позвольте мне попытаться понять.

Таблица: STUD_NAME Кол-во: STUD_No, STUD_Name

Таблица: КЛАСС Кол-во: Class_ID, STUD_No, CLASS_NAME

Запрос должен возвращать каждого ученика и класс, который они посещают.

Вот пример, который я сделал в SQLite ... скажите мне, что не так ...

sqlite> create table stud_name (stud_no integer, stud_name text);
sqlite> create table class (class_id integer, stud_no integer, class_name text);
sqlite> insert into stud_name values (1, 'Sam');
sqlite> insert into stud_name values (2, 'Mike');
sqlite> insert into stud_name values (3, 'Pat');
sqlite> insert into stud_name values (4, 'Leena');
sqlite> insert into class values (1,1,'Physics');
sqlite> insert into class values (2,1,'Math');
sqlite> insert into class values (3,2,'Physics');
sqlite> select stud_name, class_name from stud_name s, class c
   ...> where s.stud_no = c.stud_no;
Sam|Math
Sam|Physics
Mike|Physics
...