在没有LISTAGG的Oracle SQL中按字符串列表排序

Cur*_* F. 5 sql oracle oracle11gr2

我正在使用两个实体:ItemAttribute看起来如下所示:

Item
----
itemId

Attribute
---------
attributeId
name
Run Code Online (Sandbox Code Playgroud)

一个Item具有Attributes,如在关联表中指定:

ItemAttribute
--------------
itemId
attributeId
Run Code Online (Sandbox Code Playgroud)

当这些数据到达客户端时,它将显示Item每行一行,每行将按Attribute名称列出s.例如:

Item  Attributes
----  ----------
1     A, B, C
2     A, C
3     A, B
Run Code Online (Sandbox Code Playgroud)

用户可以选择对Attributes列进行排序,因此我们需要能够按如下方式对数据进行排序:

Item  Attributes
----  ----------
3     A, B
1     A, B, C
2     A, C
Run Code Online (Sandbox Code Playgroud)

目前,我们每ItemAttribute行获得一行数据.基本上:

  SELECT Item.itemId,
         Attribute.name
    FROM Item
    JOIN ItemAttribute
      ON ItemAttribute.itemId = Item.itemId
    JOIN Attribute
      ON Attribute.attributeId = ItemAttribute.attributeId
ORDER BY Item.itemId;
Run Code Online (Sandbox Code Playgroud)

产生如下结果:

itemId  name
------  ----
1       A
1       B
1       C
2       A
2       C
3       A
3       B
Run Code Online (Sandbox Code Playgroud)

实际ORDER BY条款基于用户输入.它通常是一个列,因此排序很简单,处理结果集的app端循环将Attribute名称组合成逗号分隔列表,以便在客户端上显示.但是当用户要求对该列表进行排序时,让Oracle对结果进行排序以便 - 使用上面的示例 - 我们得到:

itemId  name
------  ----
3       A
3       B
1       A
1       B
1       C
2       A
2       C
Run Code Online (Sandbox Code Playgroud)

Oracle的LISTAGG功能可用于在排序之前生成属性列表; 但是Attribute.name可以是一个非常长的字符串,并且组合列表可能大于4000个字符,这将导致查询失败.

有没有一种干净,有效的方法使用Oracle SQL(11gR2)以这种方式对数据进行排序?

Vin*_*rat 4

这里确实有两个问题:

1)如何聚合超过4000个字符的数据

聚合如此多的数据并将其显示在单列中是否明智?

无论如何,您将需要某种大型结构来显示超过 4000 个字符,例如 a CLOB您可以按照Tom Kyte 的线程之一中描述的一般准则编写自己的聚合方法(显然您需要对其进行修改,以便最终输出是 CLOB)。

我将演示一个更简单的方法,使用嵌套表和自定义函数(适用于 10g):

SQL> CREATE TYPE tab_varchar2 AS TABLE OF VARCHAR2(4000);
  2  /

Type created.

SQL> CREATE OR REPLACE FUNCTION concat_array(p tab_varchar2) RETURN CLOB IS
  2     l_result CLOB;
  3  BEGIN
  4     FOR cc IN (SELECT column_value FROM TABLE(p) ORDER BY column_value) LOOP
  5        l_result := l_result ||' '|| cc.column_value;
  6     END LOOP;
  7     return l_result;
  8  END;
  9  /

Function created.

SQL> SELECT item,
  2         concat_array(CAST (collect(attribute) AS tab_varchar2)) attributes
  3    FROM data
  4   GROUP BY item;

ITEM ATTRIBUTES
1    a b c
2    a c
3    a b
Run Code Online (Sandbox Code Playgroud)

2)大数据如何排序

不幸的是,您无法在 Oracle 中按任意大的列进行排序:存在与排序键的类型和长度相关的已知限制。

  • 尝试使用 clob 进行排序将导致ORA-00932:不一致的数据类型:预期 - 得到 CLOB。
  • 尝试使用大于数据库块大小的键进行排序(例如,如果您决定将大数据拆分为许多 VARCHAR2)将产生ORA-06502:PL/SQL:数字或值错误:字符串缓冲区太小

我建议您按属性列的前 4000 个字节排序:

SQL> SELECT * FROM (
  2     SELECT item,
  3            concat_array(CAST (collect(attribute) AS tab_varchar2)) attributes
  4       FROM data
  5      GROUP BY item
  6  ) order by dbms_lob.substr(attributes, 4000, 1);

ITEM ATTRIBUTES
3    a b
1    a b c
2    a c
Run Code Online (Sandbox Code Playgroud)