Oracle 10g 中的 LISTAGG 替代方案

Posted

技术标签:

【中文标题】Oracle 10g 中的 LISTAGG 替代方案【英文标题】:LISTAGG alternative in Oracle 10g 【发布时间】:2017-03-05 15:02:47 【问题描述】:

我是 Oracle 的新手。 卡在下面: 我有以下 2 个表:

网站:

**SiteID|SiteName** 
1      Sydney
2      Newyork
3      Delhi

人:

**RecordID|PeopleID|SiteID**
1         1        1
2         1        2
3         2        2
4         3        1
5         3        2
6         3        3

现在在我的查询中,我想要这样的输出:

**PeopleID | AssignedSites**
1          Sydney,NewYork
2          Newyork
3          Sydney,NewYork,Delhi
还有几点:

-该解决方案应该适用于 Oracle 10g 以及 11g。

-为简洁起见,我在上面的示例中给出了一小部分数据。但是,在我的 prod 场景中,一个人可以与 1000 多个位置相关联,并且可能有 1000 多个这样的人,因此解决方案不应中断案例!

我们将不胜感激。

提前致谢。

【问题讨论】:

Is there any function in oracle similar to group_concat in mysql?的可能重复 Oracle 10g 需要强调一下,因为在 Oracle 11 中引入了显而易见的解决方案 (listagg())。 是的,我提到了 Oracle 10g。 @OP - 有关 10g 的解决方案,请参阅此页面 - oracle-base.com/articles/misc/string-aggregation-techniques 为什么需要这个?我认为它不是用于报告(没有人会阅读显示 1000 多人的报告,并且每个人都有 1000 多个位置,以逗号分隔)。你需要这个做进一步处理吗?如果是这样,那么在一个以逗号分隔的长字符串中连接位置几乎肯定是错误的方法 - 很难维护,而且很可能会影响性能。 【参考方案1】:

尝试像这样使用XMLAGG

select
    p.PeopleID,
    rtrim(xmlagg(xmlelement(e, s.SiteName, ',')).extract('//text()').getclobval(), ',')
from people p
join site s on p.SiteID = s.SiteID
group by p.PeopleID;

如果您需要按特定顺序连接,例如 SiteId 的递增顺序,则在 xmlagg 中添加 order by 子句:

select
    p.PeopleID,
    rtrim(xmlagg(xmlelement(e, s.SiteName, ',')
                   order by s.SiteId).extract('//text()').getclobval(), ',')
from people p
join site s on p.SiteID = s.SiteID
group by p.PeopleID;

编辑:

如果您想显示所有分配到站点 100 的人的结果:

select p.PeopleID,
    rtrim(xmlagg(
                xmlelement(e, s.SiteName, ',') order by s.SiteId
            ).extract('//text()').getclobval(), ',')
from people p
join site s on p.SiteID = s.SiteID
join (
    select distinct PeopleID
    from people
    where siteID = 1
    ) p2 on p.PeopleID = p2.PeopleID
group by p.PeopleID;

【讨论】:

得到错误:ORA-19011:字符串缓冲区太小 19011. 00000 - “字符串缓冲区太小” *原因:请求的字符串结果太大而无法返回 *操作:获取结果改为 lob @user2948533 - 这是因为字符串限制。您可以改为获得 CLOB。更新了答案。现在可以试试吗? 完美,我只希望我的中间层代码(C#)能够处理这种数据类型(clob)。非常感谢! @user2948533 - 干杯:)。也许this 会有所帮助。 一个简单的问题:如果我必须按 SiteID 过滤上述查询,即像 SiteID='100' 这样的东西,该怎么做?我得到:ORA-00979: not a GROUP BY 表达式 00979. 00000 - “不是 GROUP BY 表达式”错误。【参考方案2】:

listagg() 是显而易见的选择,但它在 Oracle 10 中不可用。但是,即使在 Oracle 11 中,listagg() 也仅限于长度为 4,000 的字符串,并且您明确地说“Person can be associated with 1000+地点”。

有一些方法可以解决这个问题,使用 CLOB、XML,毫无疑问还有其他解决方案。然而,一个包含成千上万个字符的位置列表有什么用呢?有这么多位置,您将无法将结果放在标准的 varchar2() 字段中。

也许以这种方式在数据库中总结它们并不是解决实际问题的最佳解决方案。

【讨论】:

我想我已经接近了,只需要一点帮助:我创建了一个函数 GetSiteName,它根据 SiteID 返回站点名称。现在我使用下面的 xmlagg 我需要调用这个函数 GetSiteName: @GurV,我想我已经接近解决方案了,我已经在我的方法下面发布了,只是停留在从 xmlagg 中调用我的函数,你能帮忙吗? @Gordon,我同意,这是应用程序中现有的行为。但是您能说明一下我的以下方法吗?在此先感谢 :)【参考方案3】:

我想我已经接近它了,只需要一点帮助:我创建了一个函数 GetSiteName,它根据 SiteID 返回站点名称。现在我正在使用下面的 xmlagg,我需要在其中调用此函数 GetSiteName:

select PeopleID, rtrim (xmlagg (xmlelement (e,  clint.GetSiteName(SiteID)   || ',')).extract ('//text()'), ',') SITEIDS
from client.People group by    PeopleID;/

在从 xmlagg 内部调用函数时基本上需要帮助,有什么想法吗?

【讨论】:

【参考方案4】:

我只需要在 oracle 10g 上替代 listagg 并在此页面中找到了一个 https://oracle-base.com/articles/misc/string-aggregation-techniques。

只需使用 wm_concat,尽管它不受 oracle 支持并且已在 12c 中删除。

上面的例子:

select
    p.PeopleID,wm_concat(s.SiteName)
from people p
join site s on p.SiteID = s.SiteID
group by p.PeopleID;

【讨论】:

以上是关于Oracle 10g 中的 LISTAGG 替代方案的主要内容,如果未能解决你的问题,请参考以下文章

LISTAGG 相当于 10 克 [重复]

oracle 10g怎么实现 listagg功能

用listagg怎么替代这个写法

在 Oracle 10g 的 SQL 过程中使用游标的任何替代方法?

oracle10g没有行列转换函数的替代方法(转)

PL/SQL Oracle 10g 询问用户输入(替代变量除外)