Eu sei que isso foi respondido em algum grau com PHP e MYSQL, mas eu queria saber se alguém poderia me ensinar a abordagem mais simples para dividir uma string (delimitada por vírgulas) em várias linhas no Oracle 10g (de preferência) e 11g.
A tabela é a seguinte:
Name | Project | Error
108 test Err1, Err2, Err3
109 test2 Err1
Eu quero criar o seguinte:
Name | Project | Error
108 Test Err1
108 Test Err2
108 Test Err3
109 Test2 Err1
Eu vi algumas soluções potenciais em torno da pilha, no entanto, elas representavam apenas uma única coluna (sendo a string delimitada por vírgulas). Qualquer ajuda seria muito apreciada.
REGEXP
,XMLTABLE
eMODEL
cláusula, consulte Dividir vírgula strings delimitadas em uma tabela usando o Oracle SQLRespostas:
Esta pode ser uma maneira melhorada (também com regexp e conectar por):
EDIT : Aqui está uma explicação simples (como em "não em profundidade") da consulta.
length (regexp_replace(t.error, '[^,]+')) + 1
usaregexp_replace
para apagar qualquer coisa que não seja o delimitador (vírgula neste caso) elength +1
para obter quantos elementos (erros) existem.O
select level from dual connect by level <= (...)
usa uma consulta hierárquica para criar uma coluna com um número crescente de correspondências encontradas, de 1 ao número total de erros.Antevisão:
table(cast(multiset(.....) as sys.OdciNumberList))
faz alguma conversão de tipos de oráculo.cast(multiset(.....)) as sys.OdciNumberList
transforma várias coleções (uma coleção para cada linha no conjunto de dados original) em uma única coleção de números, OdciNumberList.table()
função transforma uma coleção em um conjunto de resultados.FROM
sem uma junção cria uma junção cruzada entre seu conjunto de dados e o multiset. Como resultado, uma linha no conjunto de dados com 4 correspondências se repetirá 4 vezes (com um número crescente na coluna chamada "valor_coluna").Antevisão:
trim(regexp_substr(t.error, '[^,]+', 1, levels.column_value))
usa ocolumn_value
como o parâmetro nth_appearance / ocurrence pararegexp_substr
.t.name, t.project
como um exemplo) para facilitar a visualização.Algumas referências aos documentos da Oracle:
fonte
'[^,]+'
para analisar strings não retorna o item correto se houver um elemento nulo na lista. Veja aqui para mais informações: stackoverflow.com/questions/31464275/…regexp_count(t.error, ',')
vez delength (regexp_replace(t.error, '[^,]+'))
, o que pode trazer outra melhoria de desempenhoexpressões regulares são uma coisa maravilhosa :)
fonte
Name
s estão conectadas, o que pode ser visto se você removerdistinct
. Infelizmente, acrescentandoand Name = prior Name
àsconnect by
causas da cláusulaORA-01436: CONNECT BY loop in user data
.ORA-01436
erro adicionandoAND name = PRIOR name
(ou qualquer que seja a chave primária) eAND PRIOR SYS_GUID() IS NOT NULL
Há uma grande diferença entre os dois abaixo:
Se você não restringir as linhas, a cláusula CONNECT BY produzirá várias linhas e não dará a saída desejada.
Além das expressões regulares , algumas outras alternativas estão usando:
Configuração
Usando XMLTABLE :
Usando a cláusula MODEL :
fonte
('"' || REPLACE(text, ',', '","') || '"')
e os colchetes não podem ser removidos? Documentos Oracle ([ docs.oracle.com/database/121/SQLRF/functions268.htm ) não são claros para mim. É issoXQuery_string
?Mais alguns exemplos do mesmo:
Além disso, pode usar DBMS_UTILITY.comma_to_table & table_to_comma: http://www.oracle-base.com/articles/9i/useful-procedures-and-functions-9i.php#DBMS_UTILITY.comma_to_table
fonte
comma_to_table()
só funciona com tokens que se enquadram nas convenções de nomenclatura de objetos de banco de dados Oracle. Vai arremessar em uma corda,'123,456,789'
por exemplo.Eu gostaria de propor uma abordagem diferente usando uma função de tabela PIPELINED. É um pouco semelhante à técnica de XMLTABLE, exceto que você está fornecendo sua própria função personalizada para dividir a sequência de caracteres:
Resultados:
O problema com esse tipo de abordagem é que muitas vezes o otimizador não sabe a cardinalidade da função da tabela e terá que fazer uma estimativa. Isso pode ser potencialmente prejudicial aos seus planos de execução, portanto, essa solução pode ser estendida para fornecer estatísticas de execução para o otimizador.
Você pode ver essa estimativa do otimizador executando um PLANO DE EXPLICAÇÃO na consulta acima:
Mesmo que a coleção tenha apenas 3 valores, o otimizador estimou 8.168 linhas para ela (valor padrão). Isso pode parecer irrelevante no início, mas pode ser o suficiente para o otimizador decidir por um plano abaixo do ideal.
A solução é usar as extensões do otimizador para fornecer estatísticas para a coleção:
Testando o plano de execução resultante:
Como você pode ver, a cardinalidade no plano acima não é mais o valor estimado de 8196. Ainda não está correto porque estamos passando uma coluna em vez de um literal de string para a função.
Alguns ajustes no código da função seriam necessários para fornecer uma estimativa mais próxima neste caso específico, mas acho que o conceito geral é explicado muito bem aqui.
A função str2tbl usada nesta resposta foi desenvolvida originalmente por Tom Kyte: https://asktom.oracle.com/pls/asktom/f?p=100:11:0:::::P11_QUESTION_ID:110612348061
O conceito de associação de estatísticas com tipos de objeto pode ser explorado posteriormente lendo este artigo: http://www.oracle-developer.net/display.php?id=427
A técnica descrita aqui funciona em 10g +.
fonte
REGEXP_COUNT não foi adicionado até o Oracle 11i. Aqui está uma solução Oracle 10g, adotada da solução de Art.
fonte
A partir do Oracle 12c, você pode usar
JSON_TABLE
eJSON_ARRAY
:E consulta:
Resultado:
db <> demonstração de violino
fonte
Aqui está uma implementação alternativa usando XMLTABLE que permite a conversão para diferentes tipos de dados:
... ou se suas strings delimitadas forem armazenadas em uma ou mais linhas de uma tabela:
fonte
Eu gostaria de adicionar outro método. Este usa querys recursivas, algo que não vi nas outras respostas. É suportado pela Oracle desde 11gR2.
É bastante flexível com o caráter de divisão. Basta alterá-lo nas
INSTR
chamadas.fonte
Sem usar conectar por ou regexp :
fonte
Eu tive o mesmo problema, e xmltable me ajudou:
SELECT id, trim (COLUMN_VALUE) text FROM t, xmltable (('"' || REPLACE (text, ',', '", "') || '"'))
fonte
No Oracle 11g e posterior, você pode usar uma subconsulta recursiva e funções de string simples (que podem ser mais rápidas do que expressões regulares e subconsultas hierárquicas correlacionadas):
Configuração do Oracle :
Consulta :
Produto :
db <> fiddle aqui
fonte
Eu usei a função DBMS_UTILITY.comma_to _table, na verdade, está funcionando no código a seguir
eu tinha usado minha própria tabela e nomes de coluna
fonte
comma_to_table()
só funciona com tokens que se enquadram nas convenções de nomenclatura de objetos de banco de dados Oracle. Vai arremessar em uma corda,'123,456,789'
por exemplo.