5

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

Item
----
itemId

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

一个Itemhas Attributes,在关联表中指定:

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子句基于用户输入。它通常是单列,因此排序很简单,处理结果集的应用程序端循环将Attribute名称组合成一个逗号分隔的列表,以便在客户端显示。但是当用户要求对该列表进行排序时,最好让 Oracle 对结果进行排序,这样——使用上面的示例——我们会得到:

itemId  name
------  ----
3       A
3       B
1       A
1       B
1       C
2       A
2       C

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

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

4

3 回答 3

4

这里真的有两个问题:

1)如何聚合4000多字符的数据

汇总这么多数据并将其显示在单个列中是否明智?

无论如何,您将需要某种大型结构来显示超过 4000 个字符,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

2)如何对大数据进行排序

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

  • 尝试使用 clob 进行排序将导致ORA-00932:不一致的数据类型:预期 - 得到 CLOB。
  • 尝试使用大于数据库块大小的键进行排序(例如,如果您决定将大数据拆分为许多 VARCHAR2)将产生ORA-06502:PL/SQL: numeric or value error: string buffer too small

我建议您按属性列的前 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
于 2012-07-18T14:12:40.097 回答
1

正如文森特已经说过的,排序键是有限的(没有 CLOB,最大块大小)。

我可以提供一个稍微不同的解决方案,它在 10g 和更新版本中开箱即用,不需要使用 XMLAgg 的自定义函数和类型:

with ItemAttribute  as (
 select 'name'||level name
        ,mod(level,3) itemid
   from dual
  connect by level < 2000
)
,ItemAttributeGrouped as (
 select xmlagg(xmlparse(content name||' ' wellformed) order by name).getclobval() attributes
       ,itemid
   from ItemAttribute
  group by itemid
 )
select itemid
      ,attributes
      ,dbms_lob.substr(attributes,4000,1) sortkey
  from ItemAttributeGrouped
order by dbms_lob.substr(attributes,4000,1)
;  
于 2012-07-18T15:51:19.853 回答
0

清洁是主观的,需要检查效率(但它仍然只出现一次,所以可能不会更糟),但是如果你对任何项目可能的属性数量有一个有限的上限 - 或者在潜在客户您必须考虑订购多少 - 如果您可以使用多个lead调用来执行此操作:

SELECT itemId, name
  FROM (
    SELECT itemId, name, min(dr) over (partition by itemId) as dr
      FROM (
        SELECT itemId, name,
            dense_rank() over (order by name, name1, name2, name3, name4) as dr
          FROM (
            SELECT Item.itemId,
                     Attribute.name,
                     LEAD(Attribute.name, 1)
                         OVER (PARTITION BY Item.itemId
                             ORDER BY Attribute.name) AS name1,
                     LEAD(Attribute.name, 2)
                         OVER (PARTITION BY Item.itemId
                             ORDER BY Attribute.name) AS name2,
                     LEAD(Attribute.name, 3)
                         OVER (PARTITION BY Item.itemId
                             ORDER BY Attribute.name) AS name3,
                     LEAD(Attribute.name, 4)
                         OVER (PARTITION BY Item.itemId
                             ORDER BY Attribute.name) AS name4
                FROM Item
                JOIN ItemAttribute
                  ON ItemAttribute.itemId = Item.itemId
                JOIN Attribute
                  ON Attribute.attributeId = ItemAttribute.attributeId
               )
          )
      )
ORDER BY dr, name;

因此,内部查询正在获取您关心的两个值,并使用四个lead调用(仅作为示例,因此这可以根据前五个属性名称的最大值进行排序,但当然可以通过添加更多来扩展!)了解每个项目还有什么。有了你的数据,这给出了:

    ITEMID NAME       NAME1      NAME2      NAME3      NAME4
---------- ---------- ---------- ---------- ---------- ----------
         1 A          B          C
         1 B          C
         1 C
         2 A          C
         2 C
         3 A          B
         3 B

下一个查询对这五个有序属性名称进行 a ,它为每个anddense_rank分配一个等级,给出:itemIDname

    ITEMID NAME               DR
---------- ---------- ----------
         1 A                   1
         1 B                   4
         1 C                   6
         2 A                   3
         2 C                   6
         3 A                   2
         3 B                   5

下一个查询使用 的分析版本查找每个的计算dr值的最小值,因此 each得到、得到和得到。(您可以通过选择来组合这两个级别,但这(甚至)不太清楚)。itemIdminitemID=1min(dr) = 1itemId=23itemId=32min(dense_rank(...))

最后的外部查询使用每个项目的最小排名来进行实际排序,给出:

    ITEMID NAME
---------- ----------
         1 A
         1 B
         1 C
         3 A
         3 B
         2 A
         2 C
于 2012-07-18T14:48:29.663 回答