-1

谁能帮我查询一下在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
4

5 回答 5

1

您应该能够使用 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

希望有帮助!

于 2012-02-23T06:41:26.423 回答
1
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 的道具

于 2012-02-23T05:08:08.167 回答
1

对于 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

从 11g 开始,您可以使用listagg. wm_concat有关字符串聚合技术的不错列表,请参见此处

于 2012-02-23T05:09:07.103 回答
0

问题的格式很糟糕,但让我试着理解。

表:STUD_NAME 列:STUD_No,STUD_Name

表:CLASS 列: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
于 2012-02-23T03:36:42.327 回答
0

好的,在 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。

让我知道!

于 2012-02-23T04:41:21.733 回答