Estou tentando usar a LISTAGG
função no Oracle. Eu gostaria de obter apenas os valores distintos para essa coluna. Existe uma maneira de obter apenas os valores distintos sem criar uma função ou procedimento?
col1 col2 Created_by 1 2 Smith 1 2 John 1 3 Ajay 1 4 Ram 1 5 Jack
Preciso selecionar col1 e LISTAGG
col2 (a coluna 3 não é considerada). Quando faço isso, obtenho algo assim como resultado de LISTAGG
: [2,2,3,4,5]
Preciso remover o duplicado '2' aqui; Preciso apenas dos valores distintos de col2 contra col1.
sql
oracle
aggregate-functions
listagg
Priyanth
fonte
fonte
Respostas:
19c e mais tarde:
18c e anteriores:
Se precisar de mais colunas, algo como isto pode ser o que você está procurando:
fonte
listagg
for a única função de agregação na consulta, isso deve servir. Combiná-lo com outras funções de agregação, no entanto, é mais complicado.Veja como resolver seu problema.
retorna
2,2.1,3,4
Do oracle 19C é construído veja aqui
A partir de 18C e anteriores, tente dentro do grupo, consulte aqui
Caso contrário, use expressões regulares
RESPOSTA abaixo:
Nota: O procedimento acima funcionará na maioria dos casos - a lista deve ser classificada, você pode ter que cortar todos os espaços à direita e à esquerda, dependendo de seus dados.
Se você tiver muitos itens em um grupo> 20 ou tamanhos de string grandes, você poderá encontrar o limite de tamanho de string do oráculo 'o resultado da concatenação de string é muito longo'.
No oracle 12cR2 você pode suprimir este erro, veja aqui . Como alternativa, coloque um número máximo de membros em cada grupo. Isso só funcionará se não houver problema em listar apenas os primeiros membros. Se você tiver strings de variáveis muito longas, isso pode não funcionar. você terá que experimentar.
Outra solução (não tão simples) para evitar o limite de tamanho da string do oráculo - o tamanho da string é limitado a 4000. Graças a esta postagem aqui pelo usuário 3465996
V1 - alguns casos de teste - FYI
V2 - itens contidos em itens, por exemplo. 2,21
v3 - regex graças a Igor! funciona todos os casos.
fonte
ORA-01489: result of string concatenation is too long
.a,b,b,b,b,c
se tornariaa,b,b,c
:-( (Oracle 11.2)regexp_replace(your_string, '([^,]+)(,\1)*(,|$)', '\1\3')
você pode usar a
wm_concat
função não documentada .esta função retorna a coluna clob, se você quiser, pode usar
dbms_lob.substr
para converter clob em varchar2.fonte
wm_concat(distinct x)
?wm_concat
. Consulte Por que não usar a função WM_CONCAT no Oracle? .Superei esse problema agrupando os valores primeiro e, em seguida, fazendo outra agregação com o listagg. Algo assim:
apenas um acesso completo à tabela, relativamente fácil de expandir para consultas mais complexas
fonte
Se a intenção é aplicar essa transformação a várias colunas, estendi a solução de a_horse_with_no_name:
Este é o Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - Produção de 64 bits.
Não consegui usar STRAGG porque não há como DISTINTO e ORDEM.
O desempenho é dimensionado linearmente, o que é bom, pois estou adicionando todas as colunas de interesse. O procedimento acima levou 3 segundos para 77 mil linhas. Por apenas um rollup, 0,172 segundos. Eu sei que havia uma maneira de distinguir várias colunas em uma tabela em uma passagem.
fonte
Se você deseja valores distintos em várias colunas, deseja controle sobre a ordem de classificação, não deseja usar uma função não documentada que pode desaparecer e não deseja mais de uma verificação completa da tabela, você pode achar esta construção útil:
fonte
Que tal criar uma função dedicada que tornará a parte "distinta":
E então use-o para fazer a agregação:
fonte
Para contornar o problema do comprimento da string, você pode usar
XMLAGG
qual é semelhante a,listagg
mas retorna um clob.Você pode então analisar usando
regexp_replace
e obter os valores exclusivos e, em seguida, transformá-lo novamente em uma string usandodbms_lob.substr()
. Se você tiver uma grande quantidade de valores distintos, ainda assim ficará sem espaço, mas para muitos casos o código abaixo deve funcionar.Você também pode alterar os delimitadores que usa. No meu caso, eu queria '-' em vez de ',' mas você deve ser capaz de substituir os travessões no meu código e usar vírgulas, se quiser.
fonte
Refinando ainda mais a correção de @YoYo para abordagem baseada em row_number () de @ a_horse_with_no_name usando DECODE vs CASE ( eu vi aqui ). Vejo que @Martin Vrbovsky também tem essa resposta de abordagem de caso.
fonte
O próximo Oracle 19c oferecerá suporte
DISTINCT
comLISTAGG
.EDITAR:
Oracle 19C LISTAGG DISTINCT
fonte
Alguém já pensou em usar uma cláusula PARTITION BY? Funcionou para mim nesta consulta para obter uma lista de serviços de aplicativo e o acesso.
Tive de cortar minha cláusula where para o NDA, mas essa é a ideia.
fonte
LISTAGG
. Parece que você teria apenas umT.ACCESS_MODE
por linha, já que está agrupando por ele?Acho que isso poderia ajudar - CASE o valor das colunas para NULL se for duplicado - então não é anexado à string LISTAGG:
Resulta em:
fonte
listagg () ignora valores NULL, então em uma primeira etapa você poderia usar a função lag () para analisar se o registro anterior tinha o mesmo valor, se sim então NULL, senão 'novo valor'.
Resultados
Observe que o segundo 2 é substituído por NULL. Agora você pode envolver um SELECT com listagg ().
Resultado
Você também pode fazer isso em várias colunas.
Resultado
fonte
Você pode fazer isso por meio da substituição RegEx. Aqui está um exemplo:
Também postado aqui: Oracle - valores Listagg exclusivos
fonte
Use a função listagg_clob criada assim:
fonte
Eu escrevi uma função para lidar com isso usando expressões regulares. Os parâmetros in são: 1) a própria chamada listagg 2) Uma repetição do delimitador
Agora você não precisa repetir a expressão regular toda vez que fizer isso, simplesmente diga:
fonte
Se você não precisa de uma ordem específica de valores concatenados e o separador pode ser uma vírgula, você pode fazer:
fonte
Eu precisei de uma versão DISTINTA disso e fiz esta funcionar.
fonte
Um aspecto irritante com
LISTAGG
isso é que se o comprimento total da string concatenada exceder 4.000 caracteres (limite paraVARCHAR2
em SQL), o erro abaixo é gerado, o que é difícil de gerenciar nas versões do Oracle até 12.1Um novo recurso adicionado em 12cR2 é a
ON OVERFLOW
cláusula deLISTAGG
. A consulta que inclui esta cláusula seria semelhante a:O procedimento acima restringirá a saída a 4000 caracteres, mas não gerará o
ORA-01489
erro.Estas são algumas das opções adicionais da
ON OVERFLOW
cláusula:ON OVERFLOW TRUNCATE 'Contd..'
: Isso será exibido'Contd..'
no final da string (o padrão é...
)ON OVERFLOW TRUNCATE ''
: Isso exibirá os 4000 caracteres sem nenhuma string de terminação.ON OVERFLOW TRUNCATE WITH COUNT
: Isso exibirá o número total de caracteres no final, após os caracteres de terminação. Por exemplo:- '...(5512)
'ON OVERFLOW ERROR
: Se você espera que oLISTAGG
falhe com oORA-01489
erro (que é o padrão de qualquer maneira).fonte
Implementei esta função armazenada:
Sinto muito, mas em alguns casos (para um conjunto muito grande), o Oracle pode retornar este erro:
mas acho que este é um bom ponto de partida;)
fonte
select col1, listaggr(col2,',') within group(Order by col2) from table group by col1
significando agregar as strings (col2) na lista mantendo a ordem n, então depois lidar com as duplicatas como grupo por col1, significando mesclar duplicatas col1 em 1 grupo. talvez isto pareça limpo e simples como deveria ser e se no caso de você desejar col3 também, você precisa adicionar mais um listagg () que éselect col1, listaggr(col2,',') within group(Order by col2),listaggr(col3,',') within group(order by col3) from table group by col1
fonte
Usando
SELECT DISTINCT ...
como parte de uma subconsulta antes de chamar LISTAGG é provavelmente a melhor maneira para consultas simples, conforme observado por @a_horse_with_no_nameNo entanto, em consultas mais complexas, pode não ser possível ou fácil de fazer isso. Eu fiz isso surgir em um cenário que estava usando a abordagem top-n usando uma função analítica.
Então eu encontrei a
COLLECT
função agregada. Está documentado que o modificadorUNIQUE
ou estáDISTINCT
disponível. Somente em 10g , ele falha silenciosamente (ignora o modificador sem erro). Porém, para superar isso, a partir de outra resposta , cheguei a esta solução:Basicamente, ao usar
SET
, eu removo as duplicatas da minha coleção.Você ainda precisaria definir o
tab_typ
como um tipo de coleção básico e, no caso de aVARCHAR
, seria, por exemplo:Também como uma correção para a resposta de @a_horse_with_no_name na situação de várias colunas, onde você pode querer agregar ainda em uma terceira (ou mais) colunas:
Se você deixasse
rn = 1
como uma condição where para a consulta, agregaria outras colunas incorretamente.fonte
Muito simples - use em sua consulta uma subconsulta com uma seleção distinta:
fonte
A maneira mais simples de lidar com vários listagg's é usar 1 WITH (fator de subconsulta) por coluna contendo um listagg dessa coluna a partir de um select distinto:
Que dá:
fonte