Oracle
 sql >> Base de données >  >> RDS >> Oracle

Agréger des valeurs distinctes dans des collections dans SQL GROUP BY

Adapté de ma réponse à une autre question.

Configuration d'Oracle :

CREATE OR REPLACE TYPE VARCHAR2s_Table IS TABLE OF VARCHAR2(100);
/

Créez un type d'agrégation défini par l'utilisateur :

CREATE OR REPLACE TYPE Varchar2sTableUnion AS OBJECT(
  list VARCHAR2s_Table,

  STATIC FUNCTION ODCIAggregateInitialize(
    ctx         IN OUT Varchar2sTableUnion
  ) RETURN NUMBER,

  MEMBER FUNCTION ODCIAggregateIterate(
    self        IN OUT Varchar2sTableUnion,
    value       IN     VARCHAR2s_Table
  ) RETURN NUMBER,

  MEMBER FUNCTION ODCIAggregateTerminate(
    self        IN OUT Varchar2sTableUnion,
    returnValue    OUT VARCHAR2s_Table,
    flags       IN     NUMBER
  ) RETURN NUMBER,

  MEMBER FUNCTION ODCIAggregateMerge(
    self        IN OUT Varchar2sTableUnion,
    ctx         IN OUT Varchar2sTableUnion
  ) RETURN NUMBER
);
/

CREATE OR REPLACE TYPE BODY Varchar2sTableUnion
IS
  STATIC FUNCTION ODCIAggregateInitialize(
    ctx         IN OUT Varchar2sTableUnion
  ) RETURN NUMBER
  IS
  BEGIN
    ctx := Varchar2sTableUnion( NULL );
    RETURN ODCIConst.SUCCESS;
  END;

  MEMBER FUNCTION ODCIAggregateIterate(
    self        IN OUT Varchar2sTableUnion,
    value       IN     VARCHAR2s_Table
  ) RETURN NUMBER
  IS
  BEGIN
    IF value IS NULL THEN
      NULL;
    ELSIF self.list IS NULL THEN
      self.list := value;
    ELSE
      self.list := self.list MULTISET UNION DISTINCT value;
    END IF;
    RETURN ODCIConst.SUCCESS;
  END;

  MEMBER FUNCTION ODCIAggregateTerminate(
    self        IN OUT Varchar2sTableUnion,
    returnValue    OUT VARCHAR2s_Table,
    flags       IN     NUMBER
  ) RETURN NUMBER
  IS
  BEGIN
    returnValue := self.list;
    RETURN ODCIConst.SUCCESS;
  END;

  MEMBER FUNCTION ODCIAggregateMerge(
    self        IN OUT Varchar2sTableUnion,
    ctx         IN OUT Varchar2sTableUnion
  ) RETURN NUMBER
  IS
  BEGIN
    IF self.list IS NULL THEN
      self.list := ctx.list;
    ELSIF ctx.list IS NULL THEN
      NULL;
    ELSE
      self.list := self.list MULTISET UNION DISTINCT ctx.list;
    END IF;
    RETURN ODCIConst.SUCCESS;
  END;
END;
/

Créez une fonction d'agrégation définie par l'utilisateur :

CREATE FUNCTION MULTISET_UNION( list VARCHAR2s_Table )
RETURN VARCHAR2s_Table
PARALLEL_ENABLE AGGREGATE USING Varchar2sTableUnion;
/

Requête :

Ensuite, vous pouvez simplement l'utiliser pour effectuer l'agrégation dans votre requête :

WITH test_data (id, a_list) AS
       (SELECT 1,
               varchar2s_table ('A', 'B', 'C')
        FROM   DUAL
        UNION ALL
        SELECT 1,
               varchar2s_table ('C', 'D', 'E')
        FROM   DUAL)
SELECT id,
       MULTISET_UNION( a_list )
FROM   test_data
GROUP BY id

Sortie :

ID MULTISET_UNION(A_LIST)
-- -------------------------------------------
 1 SCHEMA.VARCHAR2S_TABLE('A','B','C','D','E')