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

标签 sql oracle oracle11gr2

我正在与两个实体合作:ItemAttribute , 如下所示:

Item
----
itemId

Attribute
---------
attributeId
name

ItemAttributes ,如关联表中所指定:
ItemAttribute
--------------
itemId
attributeId

当此数据到达客户端时,它将显示为每 Item 一行。 , 每行都会有一个 Attribute 的列表s 的名字。例如:
Item  Attributes
----  ----------
1     A, B, C
2     A, C
3     A, B

用户可以选择对 Attributes 进行排序。列,因此我们需要能够按如下方式对数据进行排序:
Item  Attributes
----  ----------
3     A, B
1     A, B, C
2     A, C

目前,我们每 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;

产生的结果如下:
itemId  name
------  ----
1       A
1       B
1       C
2       A
2       C
3       A
3       B

实际ORDER BY子句基于用户输入。通常是单列,所以排序比较简单,处理结果集的app端循环结合了Attribute名称转换为逗号分隔的列表,以便在客户端上显示。但是当用户要求对该列表进行排序时,最好让 Oracle 对结果进行排序,这样——使用上面的例子——我们会得到:
itemId  name
------  ----
3       A
3       B
1       A
1       B
1       C
2       A
2       C

甲骨文LISTAGG函数可用于在排序之前生成属性列表;然而 Attribute.name可以是很长的字符串,并且组合列表有可能大于4000个字符,这会导致查询失败。

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

最佳答案

这里真的有两个问题:
1)如何聚合超过4000个字符的数据
聚合这么多数据并将其显示在单列中是否明智?
无论如何,您将需要某种大型结构来显示超过 4000 个字符,例如 CLOB例如。您可以按照 Tom Kyte's thread 之一中描述的一般准则编写自己的聚合方法。 (显然您需要修改它,以便最终输出是一个 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
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
    

    关于sql - 在没有 LISTAGG 的 Oracle SQL 中按字符串列表排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11541383/

    相关文章:

    sql - PostgreSQL中的for循环调用API

    Oracle 中的 SQL 枢轴查询

    oracle - 如何从oracle 11g回收站中删除表?

    sql - Oracle查找给定索引名称的表名

    sql - 使用 SQL 查找重症监护婴儿的数量

    sql - PostgreSQL:如何从函数内部设置 search_path?

    如果记录在其他表中不存在,则 MySQL 更新

    mysql - 选择删除分类广告最多的用户以及分类广告数量

    sql - 寻找最长的连胜纪录

    c# - 使用 Oracle 数据库配置 ASP.NET MVC 4 应用程序