SQL - Как я могу получить несколько значений из другой таблицы, чтобы поместиться в одной строке в операторе выбора? - PullRequest
2 голосов
/ 06 декабря 2010

Я уверен, что есть простой ответ на этот вопрос, но я провел последние 3 часа в поисках Google безрезультатно. Итак, у нас есть две три таблицы - курсы, студенты и courses_students

courses_students содержит первичные ключи курсов и студентов и предназначена для разрыва отношений m: m.

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

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

courseid     coursename       student
------------ ---------------- ---------------
1            math             john jackson
                              jack johnson
                              john smith
2            english          jane doe
                              michael thomas

и т.д ... Пожалуйста, помогите!

спасибо!

p.s. я использую оракул

Ответы [ 6 ]

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

Есть несколько разных подходов к этому.Самым простым является презентация: решите это на дисплее переднего конца.В SQL * Plus это было бы ключевое слово BREAK:

SQL> BREAK ON courseid ON coursename
SQL>
SQL> select c.courseid
  2         , c.coursename
  3         , s.studentname
  4  from  courses c
  5          join course_students cs
  6              on ( cs.courseid = c.courseid )
  7          join students s
  8              on ( s.studentid = cs.studentid )
  9  /

  COURSEID COURSENAME STUDENTNAME
---------- ---------- --------------------
         1 math       john smith
                      jack jackson
                      john jackson
         2 english    michael thomas
                      jane doe

SQL>

Другой подход заключается в использовании встроенного курсора:

SQL> select c.courseid
  2         , c.coursename
  3         , cursor (select  s.studentname
  4                   from course_students cs
  5                  join students s
  6                      on ( s.studentid = cs.studentid )
  7                  where cs.courseid = c.courseid
  8                  )
  9  from  courses c
 10  /

  COURSEID COURSENAME CURSOR(SELECTS.STUDE
---------- ---------- --------------------
         1 math       CURSOR STATEMENT : 3

CURSOR STATEMENT : 3

STUDENTNAME
--------------------
john smith
john jackson
jack jackson

         2 english    CURSOR STATEMENT : 3

CURSOR STATEMENT : 3

STUDENTNAME
--------------------
jane doe
michael thomas


SQL>

Мы можем обсудить, действительно ли это считается «одной строкой»":)

Наконец, у нас есть методы агрегирования строк.Существует несколько различных способов нарезки этой конкретной капусты, потому что - невероятно - только в самом последнем выпуске Oracle предоставил стандартную встроенную функцию для этого.Поскольку я не на 11gR2, я буду использовать WM_CONCAT () вместо LISTAGG ():

SQL> select c.courseid
  2         , c.coursename
  3         , wm_concat(s.studentname) as studentnames
  4  from  courses c
  5          join course_students cs
  6              on ( cs.courseid = c.courseid )
  7          join students s
  8              on ( s.studentid = cs.studentid )
  9  group by c.courseid
 10             , c.coursename
 11  /

  COURSEID COURSENAME STUDENTNAMES
---------- ---------- ---------------------------------------------
         1 math       john smith,john jackson,jack jackson
         2 english    jane doe,michael thomas

SQL>

Сайт Oracle-Base Тима Холла имеет сводку всех опций агрегации строк. Узнать больше .

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

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

Declare
   sql_txt      Varchar2(4000);
   Rec_cnt      Number;
Begin
   Select Count(*)
     Into Rec_Cnt
     From User_Types
    Where Type_Name = 'VCARRAY'
      And Typecode = 'COLLECTION';

  If Rec_Cnt = 0 Then
     EXECUTE IMMEDIATE 'CREATE OR REPLACE TYPE vcArray as table of varchar2(32000)';
  END IF;
END;  
/

CREATE OR REPLACE TYPE comma_list_agr_type as object
  (
     data  vcArray,

     static function
          ODCIAggregateInitialize(sctx IN OUT comma_list_agr_type )
          return number,

     member function
          ODCIAggregateIterate(self IN OUT comma_list_agr_type ,
                               value IN varchar2 )
          return number,

     member function
          ODCIAggregateTerminate(self IN comma_list_agr_type,
                                 returnValue OUT  varchar2,
                                 flags IN number)
          return number,

     member function
          ODCIAggregateMerge(self IN OUT comma_list_agr_type,
                             ctx2 IN comma_list_agr_type)
          return number
  );
/


CREATE OR REPLACE TYPE BODY comma_list_agr_type
  is

  static function ODCIAggregateInitialize(sctx IN OUT comma_list_agr_type)
  return number
  is
  begin
      sctx := comma_list_agr_type( vcArray() );
      return ODCIConst.Success;
  end;

  member function ODCIAggregateIterate(self IN OUT comma_list_agr_type,
                                       value IN varchar2 )
  return number
  is
  begin
      data.extend;
      data(data.count) := value;
      return ODCIConst.Success;
  end;

  member function ODCIAggregateTerminate(self IN comma_list_agr_type,
                                         returnValue OUT varchar2,
                                         flags IN number)
  return number
  is
      l_data varchar2(32000);
  begin
      for x in ( select column_value from TABLE(data) order by 1 )
      loop
              l_data := l_data || ',' || x.column_value;
      end loop;
      returnValue := ltrim(l_data,',');
      return ODCIConst.Success;
  end;

  member function ODCIAggregateMerge(self IN OUT comma_list_agr_type,
                                     ctx2 IN comma_list_agr_type)
  return number
  is
  begin -- not really tested ;)
      for i in 1 .. ctx2.data.count
      loop
              data.extend;
              data(data.count) := ctx2.data(i);
      end loop;
      return ODCIConst.Success;
  end;
  end;
/

CREATE OR REPLACE FUNCTION comma_list(input varchar2 )
  RETURN varchar2
  PARALLEL_ENABLE AGGREGATE USING comma_list_agr_type;
/

GRANT EXECUTE ON COMMA_LIST to someuser
/
1 голос
/ 06 декабря 2010

SQL на самом деле не имеет дело с иерархическими данными, он имеет дело с наборами. Это лучше обрабатывается в 2 запросах: один возвращает информацию о курсе, а второй возвращает студентов в курсе.

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

sql отстой (правда .. ??? !!),

Первое, что вы должны уточнить, состоит в том, состоит ли цель вопроса в том, чтобы представить данные в этом формате (или) сам запрос должен показать данные в этом формате.

a) если это просто презентация, взгляните на SQLPLUS "break on". Это позволяет вам разбить определенный столбец и не повторять одни и те же значения, если значение не изменилось.

b) 1) Если это запрос, который должен выводить данные в этом формате, тогда посмотрите на подход, предложенный выше. если вы хотите изучить больше возможностей, 2) посмотрите функции опережения и опережения. Вы можете увидеть значение столбца в предыдущей строке, используя эти функции. Если они совпадают, вы увидите нулевое значение, иначе фактическое значение.

Imp: Для вариантов a) и b (2) вы должны упорядочить результаты, используя предложение order by. (Почему? ..)

Также проверьте эту ссылку: Как удалить повторяющиеся значения столбцов из отчета

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

В какой версии Oracle вы находитесь?Случайно, если вы используете Oracle DB 11g R2, взгляните на listagg.

SQL> select deptno,
  2         listagg( ename, '; ' )
  3         within group
  4         (order by ename) enames
  5     from hr.employees
  6    group by deptno
  7    order by deptno
  8   /

  DEPTNO   ENAMES
---------  --------------------
  10       CLARK; KING; MILLER
  20       ADAMS; FORD; JONES;
           SCOTT; SMITH
  30       ALLEN; BLAKE;
           JAMES; MARTIN;
           TURNER; WARD

В вашем случае вы должны сделать это для таблицы курса.В предыдущих версиях вы можете делать с CONNECT BY предложением.Подробнее о listagg.

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

если вам просто нужны результаты в запросе, почему бы и нет?

with courses as
(select 'biology' coursename, 1 courseid from dual
union
 select 'chemistry' coursename, 2 courseid from dual)
 ,
 students as
 (select 'Sally' studentName, 1 studentId from dual
  union
  select 'Jonny' studentName, 2 studentId from dual
  union
  select 'Tom' studentName, 3 studentId from dual
  union
  select 'Jane' studentName, 4 studentId from dual
  ) ,
  courses_students as
  (select 1 studentId, 1 courseId from dual
    union
    select 1 studentId, 2 courseId from dual
    union
    select 2 studentId, 1 courseId from dual
    union
    select 3 studentId, 2 courseId from dual
  )
  select c.courseName ,
         cursor(select s.StudentName
                        from students s
                             inner join
                              courses_students cs
                                on s.studentId = cs.studentId
                      where cs.courseId = c.courseId) students
        from courses c ;

если нет типов, но это сработает.

COURSENAME STUDENTS 
---------- -------- 
biology    STUDENTNAME  
           -----------  
           Sally        
           Jonny        


chemistry  STUDENTNAME  
           -----------  
           Sally        
           Tom          

все в одном запросе и в буквальном смысле ничего особенного (просто с помощью оператора CURSOR )

, если вы используете 11gr2 ListAgg прекрасно работает

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