Почему функция JSON_ARRAYAGG возвращает не уникальные значения в JSON массив?

Пытаюсь поместить результат одноколоночного запроса в массив JSON, чтобы его использовать на веб-сервере.

Пробую как-то так:

SELECT col FROM tbl
/       
COL
---
A
C
A
B

SELECT JSON_OBJECT(
  'ArrayKey' VALUE JSON_ARRAYAGG( col ) 
) AS jsonResult
FROM (SELECT DISTINCT col as col
       FROM tbl);

JSONRESULT
--------------------------------
{"ArrayKey":["A","C","A","B"]}

Этот запрос возвращает в результате массив, который содержит все значения из столбца, т.е. игнорирует DISTINCT в подзапросе. Без выражения JSON_ARRAYAGG результат уникален:

SELECT DISTINCT col as col FROM tbl
/
COL
---
B
C
A

Пытался также поместить DISTINCT внутрь JSON_ARRAYAGG, но результат тот же:

SELECT JSON_OBJECT (
  'ArrayKey' VALUE JSON_ARRAYAGG(DISTINCT col) 
) AS jsonResult
FROM (SELECT DISTINCT col AS col FROM tbl);

Что не так в моем коде? Из-за чего массив выводит все значения, а не отдельные?

На db<>fiddle.


Свободный перевод вопроса How to return distinct values in a JSON_ARRAYAGG от участника @jwas


Ответы (1 шт):

Автор решения: 0xdb

Похоже на баг в выпуске 18c. В версии 19.3.0 больше не воспроизводится:

Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0

SQL> SELECT JSON_OBJECT (
  2    'ArrayKey' VALUE JSON_ARRAYAGG(col)
  3  ) AS jsonResult
  4  FROM (SELECT DISTINCT col as col FROM tbl);

JSONRESULT
--------------------------------------------------------------------------------
{"ArrayKey":["B","C","A"]}

Для тех, у кого 18c, в качестве обходного пути можно воспользоваться GROUP BY вместо DISTINCT. Участник @sticky bit в принятом ответе предложил использовать подсказку NO_MERGE на подзапрос, или CTE с подсказкой MATERIALIZE.

Все варианты рабочих запросов на db<>fiddle:

select json_object ('ArrayKey' value json_arrayagg (col order by col)) jsonResult
from (select col from tbl group by col)
/
SELECT /*+NO_MERGE(x)*/ json_object(
     'ArrayKey' VALUE json_arrayagg(col)) jsonResult
FROM (SELECT DISTINCT col FROM tbl) x
/
WITH cte AS (
    SELECT /*+MATERIALIZE*/ DISTINCT col
    FROM tbl
)
SELECT json_object(
    'ArrayKey' VALUE json_arrayagg(col)) jsonResult
FROM cte
/

Участник @Beda Hammerschmidt в ответе упомянул баг:

Bug 27757725 - JSON GENERATION AGGREGATION FUNCTIONS IGNORE DISTINCT

Но бльше информации по нему не предоставил.

→ Ссылка