SQL - 如何从另一个表中获取多个值以适合select语句中的单个行?

时间:2010-12-06 20:14:55

标签: sql oracle

我确信这有一个简单的答案,但我只花了最后3个小时搜索谷歌无济于事。所以,我们有两个三个表 - 课程,学生和课程_学生

courses_students包含课程和学生的主键,可以打破m:m关系。

我的家庭作业要我写一个查询来显示特定课程的所有细节......包括该课程中所有学生的列表。我尝试使用各种类型的连接,但最终获得了多行的课程信息。

即。它应该一次显示课程的详细信息,并包括所有学生,例如

courseid     coursename       student
------------ ---------------- ---------------
1            math             john jackson
                              jack johnson
                              john smith
2            english          jane doe
                              michael thomas
等等......请帮忙!

谢谢!

P.S。我正在使用oracle

6 个答案:

答案 0 :(得分:1)

SQL并不真正处理分层数据,而是处理集合。这可以在2个查询中更好地处理 - 一个返回课程信息,一个返回课程中的学生。

答案 1 :(得分:1)

查找用户定义的聚合函数。 如果你真的需要在一列中列出它们,你可以设置一个聚合函数,它会为你做到这一点。

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
/

答案 2 :(得分:1)

有几种不同的方法可以解决这个问题。最简单的是表现形式:在前端显示器中解决它。在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>
Tim Hall的Oracle-Base网站汇总了所有字符串聚合选项。 Find out more

答案 3 :(得分:0)

如果您只是在查询中需要结果,为什么不呢?

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效果很好

答案 4 :(得分:0)

您所在的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的更多详情。

答案 5 :(得分:0)

sql suck(真的...... ??? !!),

首先要澄清的是问题的意图是以该格式呈现数据(或)查询本身是否应以此格式显示数据。

a)如果只是演示文稿,请看一下SQLPLUS“break on”。这允许你做的是在特定列上中断,如果值没有改变则不重复相同的值。

b)1)如果是以这种格式输出数据的查询,那么请查看上面“拼写”所建议的方法。如果您想探索更多选项,2)查看超前和滞后函数。您可以使用这些函数查看上一行中列的值。如果它们相同,则显示null,否则显示实际值。

Imp: 对于选项a)和b(2),您应该使用order by子句对结果进行排序。 (为什么..?)

另外,请检查此链接:How do I remove repeated column values from report