用于获取一对多关系结构的本机Oracle查询

问题描述 投票:0回答:5

任何人都可以帮我一个查询来获取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
sql oracle oracle9i string-aggregation
5个回答
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


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。请参阅here以获取一个很好的字符串聚合技术列表。


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

希望有所帮助!


0
投票

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

表:STUD_NAME Cols:STUD_No,STUD_Name

表:CLASS Cols: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

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中,我相信你可以用wm_concat替换上面的group_concat。

让我知道!

© www.soinside.com 2019 - 2024. All rights reserved.