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

Le bogue Oracle produit des valeurs agrégées en double dans JSON_ARRAYAGG

Cela semble être un bug. Le plan d'exécution ne fait allusion à aucun DISTINCT opération en cours d'application :

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |       |       |     6 (100)|          |
|   1 |  SORT GROUP BY     |      |     1 |    26 |            |          |
|*  2 |   TABLE ACCESS FULL| T2   |     1 |    26 |     3   (0)| 00:00:01 |
|   3 |  SORT GROUP BY     |      |     1 |    13 |            |          |
|   4 |   TABLE ACCESS FULL| T1   |     2 |    26 |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------

Solution 1

Utilisez un mannequin HAVING COUNT(*) = COUNT(*) prédicat :

select json_arrayagg(json_object(
  key 't1_id' value t1_id,
  key 't2' value (
    select json_arrayagg(json_object(
      key 't2_value' value t2_value
    ))
    from (
      select distinct t2.t2_value
      from t2
      where t2.t1_id = t1.t1_id
    ) t
    having count(*) = count(*) -- Workaround
  ) format json
))
from t1;

Cela produit le résultat correct :

[{
  "t1_id":1,
  "t2":[{ "t2_value":1 }]
}, {
  "t1_id":2,
  "t2":[{ "t2_value":2 }, { "t2_value":3 }]
}]

Le plan est maintenant :

------------------------------------------------------------------------------
| Id  | Operation             | Name | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |      |       |       |     7 (100)|          |
|*  1 |  FILTER               |      |       |       |            |          |
|   2 |   SORT GROUP BY       |      |     1 |    13 |            |          |
|   3 |    VIEW               |      |     1 |    13 |     4  (25)| 00:00:01 |
|   4 |     SORT UNIQUE       |      |     1 |    26 |     4  (25)| 00:00:01 | <--
|*  5 |      TABLE ACCESS FULL| T2   |     1 |    26 |     3   (0)| 00:00:01 |
|   6 |  SORT GROUP BY        |      |     1 |    13 |            |          |
|   7 |   TABLE ACCESS FULL   | T1   |     2 |    26 |     3   (0)| 00:00:01 |
------------------------------------------------------------------------------

Solution 2

Utiliser un UNION pour faire respecter la distinction :

select json_arrayagg(json_object(
  key 't1_id' value t1_id,
  key 't2' value (
    select json_arrayagg(json_object(
      key 't2_value' value t2_value
    ))
    from (
      select distinct t2.t2_value
      from t2
      where t2.t1_id = t1.t1_id
      union select null from dual where 1 = 0 -- Dummy union
    ) t
  ) format json
))
from t1;

Le plan est maintenant :

------------------------------------------------------------------------------
| Id  | Operation             | Name | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |      |       |       |     6 (100)|          |
|   1 |  SORT GROUP BY        |      |     1 |    13 |            |          |
|   2 |   VIEW                |      |     2 |    26 |     3   (0)| 00:00:01 |
|   3 |    SORT UNIQUE        |      |     2 |    26 |     3   (0)| 00:00:01 | <--
|   4 |     UNION-ALL         |      |       |       |            |          |
|*  5 |      TABLE ACCESS FULL| T2   |     1 |    26 |     3   (0)| 00:00:01 |
|*  6 |      FILTER           |      |       |       |            |          |
|   7 |       FAST DUAL       |      |     1 |       |     2   (0)| 00:00:01 |
|   8 |  SORT GROUP BY        |      |     1 |    13 |            |          |
|   9 |   TABLE ACCESS FULL   | T1   |     2 |    26 |     3   (0)| 00:00:01 |
------------------------------------------------------------------------------

Et le résultat est également correct