Мой запрос возвращает много (тысячи) строк.
Столбец l
имеет определенное значение для очень небольшого количества строк (до 10).
Для каждой такой строки я хочу вывести агрегированные значения, разделенные запятыми, очень короткого (до 5 символов) столбца varchar v
по всем этим строкам.
Для строк, не имеющих специального значения l
, я хочу просто вывести значение v
для этой строки.
Синтезированный пример той же проблемы: из первых 10000 целых чисел я хочу вывести 1,2,3,4,5,6,7,8,9
для каждого однозначного числа; этот номер для многозначного числа. (Да, глупый пример, но реальный случай имеет смысл.)
with x (v,l) as (
select to_char(level), length(to_char(level)) from dual connect by level <= 10000
)
select case l
when 1 then listagg(v,',') within group (order by v) over (partition by l)
else v
end
from x
order by 1;
Проблема в том, что функция listagg
не работает при ошибке ORA-01489: result of string concatenation is too long
.
Мне известно о пределе 4000 символов для функции listagg
, а также об обходном пути на основе xmlagg. Я просто не получаю предела, которого достаточно для данных, которые я хочу объединить, хотя его недостаточно для всех данных. В приведенном выше примере раздел из 9 однозначных чисел умещается в 4000 символов, а раздел из 9000 четырехзначных чисел - нет. Я ожидал, что выражение case
предотвратит выполнение окна для несвязанных строк, но по какой-то причине кажется, что механизм db оценивает окно для всех строк. (Также обратите внимание, что предложение order by
приводит к быстрому выполнению запроса - без него некоторые строки возвращаются до сбоя.)
Не могли бы вы объяснить причину такого поведения? Я подозреваю, что вычисление окна логически выполняется перед предложением select
, но без каких-либо доказательств. Воспроизводится на Oracle 11g, 18c и 19 (liveql).
2 ответа
Что ж, вы используете SQL
, который не является процедурным, поэтому вы не можете ожидать , что некоторые части пути кода не будут выполнены только потому, что они не используются. (Таким образом, исправление ошибки, предложенное другими способами, не принесет успеха).
В любом случае вы можете проделать часто используемый трюк, основанный на том факте, что listagg
игнорирует значения null
.
Итак, эта формулировка отлично работает:
with x (v,l) as (
select to_char(level), length(to_char(level)) from dual connect by level <= 10000
)
select nvl(listagg(case when l = 1 then v end,',') within group (order by v) over (partition by l),v) lst
from x
order by 1;
Давая
LST
------------------
1,2,3,4,5,6,7,8,9
1,2,3,4,5,6,7,8,9
..
10
100
1000
10000
Объяснение проблемы можно найти в плане выполнения (с указанием только соответствующей части)
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 35 | 4 (50)| 00:00:01 |
| 1 | SORT ORDER BY | | 1 | 35 | 4 (50)| 00:00:01 |
| 2 | WINDOW SORT | | 1 | 35 | 4 (50)| 00:00:01 |
| 3 | VIEW | | 1 | 35 | 2 (0)| 00:00:01 |
|* 4 | CONNECT BY WITHOUT FILTERING| | | | | |
| 5 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
...
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - (#keys=1) CASE "L" WHEN 1 THEN LISTAGG("V",',') WITHIN GROUP ( ORDER BY
"V") OVER ( PARTITION BY "L") ELSE "V" END [4000]
2 - (#keys=2) "L"[NUMBER,22], "V"[VARCHAR2,40], LISTAGG("V",',') WITHIN
GROUP ( ORDER BY "V") OVER ( PARTITION BY "L")[4000]
3 - "V"[VARCHAR2,40], "L"[NUMBER,22]
4 - LEVEL[4]
Таким образом, в строке 2 вычисляется listagg
(для всех строк) только для фильтрации в строке 1.
Странно, что вы получаете сообщение об ограничении 4000 символов, даже если результат не превышает 4000 символов. Возможно, вы могли бы сообщить об этом как об ошибке в службу поддержки Oracle.
Другой обходной путь - использовать логику ON OVERFLOW
функции LISTAGG
, если вы используете Oracle 12.2 или выше. Использование LISTAGG (v, ',' ON OVERFLOW TRUNCATE)
в запросе позволяет выполнить запрос без ошибок и не усекает никаких значений (по крайней мере, в примере).
Похожие вопросы
Новые вопросы
sql
Язык структурированных запросов (SQL) - это язык запросов к базам данных. Вопросы должны включать примеры кода, структуру таблицы, примеры данных и тег для используемой реализации СУБД (например, MySQL, PostgreSQL, Oracle, MS SQL Server, IBM DB2 и т. Д.). Если ваш вопрос относится исключительно к конкретной СУБД (использует определенные расширения / функции), используйте вместо этого тег этой СУБД. Ответы на вопросы, помеченные SQL, должны использовать стандарт ISO / IEC SQL.