Je sais que cela a été répondu dans une certaine mesure avec PHP et MYSQL, mais je me demandais si quelqu'un pourrait m'apprendre l'approche la plus simple pour diviser une chaîne (délimitée par des virgules) en plusieurs lignes dans Oracle 10g (de préférence) et 11g.
Le tableau est le suivant:
Name | Project | Error
108 test Err1, Err2, Err3
109 test2 Err1
Je souhaite créer ce qui suit:
Name | Project | Error
108 Test Err1
108 Test Err2
108 Test Err3
109 Test2 Err1
J'ai vu quelques solutions potentielles autour de la pile, mais elles ne représentaient qu'une seule colonne (étant la chaîne délimitée par des virgules). Toute aide serait grandement appréciée.
REGEXP
,XMLTABLE
etMODEL
, consultez Fractionner les chaînes délimitées par des virgules dans une table à l'aide d'Oracle SQLRéponses:
Cela peut être un moyen amélioré (également avec regexp et se connecter par):
EDIT : Voici une explication simple (comme dans, "pas en profondeur") de la requête.
length (regexp_replace(t.error, '[^,]+')) + 1
utiliseregexp_replace
pour effacer tout ce qui n'est pas le délimiteur (virgule dans ce cas) etlength +1
pour obtenir le nombre d'éléments (erreurs) présents.Le
select level from dual connect by level <= (...)
utilise une requête hiérarchique pour créer une colonne avec un nombre croissant de correspondances trouvées, de 1 au nombre total d'erreurs.Aperçu:
table(cast(multiset(.....) as sys.OdciNumberList))
fait du casting de types d'oracle.cast(multiset(.....)) as sys.OdciNumberList
transforme plusieurs collections (une collection pour chaque ligne de l'ensemble de données d'origine) en une seule collection de nombres, OdciNumberList.table()
fonction transforme une collection en un ensemble de résultats.FROM
sans jointure crée une jointure croisée entre votre ensemble de données et le multiset. En conséquence, une ligne dans l'ensemble de données avec 4 correspondances se répétera 4 fois (avec un nombre croissant dans la colonne nommée "valeur_colonne").Aperçu:
trim(regexp_substr(t.error, '[^,]+', 1, levels.column_value))
utilise lecolumn_value
comme paramètre nth_appearance / occurrence pourregexp_substr
.t.name, t.project
titre d'exemple) pour une visualisation facile.Quelques références à des documents Oracle:
la source
'[^,]+'
pour analyser les chaînes ne renvoie pas l'élément correct s'il y a un élément nul dans la liste. Voir ici pour plus d'informations: stackoverflow.com/questions/31464275/…regexp_count(t.error, ',')
place delength (regexp_replace(t.error, '[^,]+'))
, ce qui peut apporter une autre amélioration des performancesles expressions régulières sont une chose merveilleuse :)
la source
Name
s est connectée, ce qui peut être vu si vous supprimezdistinct
. Malheureusement ajouterand Name = prior Name
à laconnect by
clause provoqueORA-01436: CONNECT BY loop in user data
.ORA-01436
erreur en ajoutantAND name = PRIOR name
(ou quelle que soit la clé primaire) etAND PRIOR SYS_GUID() IS NOT NULL
Il y a une énorme différence entre les deux ci-dessous:
Si vous ne limitez pas les lignes, la clause CONNECT BY produira plusieurs lignes et ne donnera pas la sortie souhaitée.
Outre les expressions régulières , quelques autres alternatives utilisent:
Installer
Utilisation de XMLTABLE :
Utilisation de la clause MODEL :
la source
('"' || REPLACE(text, ',', '","') || '"')
et les crochets ne peuvent pas être supprimés? Les documents Oracle ([ docs.oracle.com/database/121/SQLRF/functions268.htm ) ne sont pas clairs pour moi. C'est çaXQuery_string
?Quelques autres exemples de la même chose:
Vous pouvez également utiliser 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
la source
comma_to_table()
ne fonctionne qu'avec des jetons qui correspondent aux conventions de dénomination des objets de base de données Oracle. Il se lancera sur une corde comme'123,456,789'
par exemple.Je voudrais proposer une approche différente utilisant une fonction de table PIPELINED. C'est un peu similaire à la technique du XMLTABLE, sauf que vous fournissez votre propre fonction personnalisée pour diviser la chaîne de caractères:
Résultats:
Le problème avec ce type d'approche est que souvent l'optimiseur ne connaîtra pas la cardinalité de la fonction de table et devra faire une estimation. Cela peut potentiellement nuire à vos plans d'exécution, cette solution peut donc être étendue pour fournir des statistiques d'exécution pour l'optimiseur.
Vous pouvez voir cette estimation de l'optimiseur en exécutant un EXPLAIN PLAN sur la requête ci-dessus:
Même si la collection n'a que 3 valeurs, l'optimiseur a estimé 8168 lignes pour elle (valeur par défaut). Cela peut sembler hors de propos au début, mais il peut suffire à l'optimiseur de décider d'un plan sous-optimal.
La solution consiste à utiliser les extensions d'optimisation pour fournir des statistiques pour la collection:
Test du plan d'exécution résultant:
Comme vous pouvez le voir, la cardinalité sur le plan ci-dessus n'est plus la valeur estimée 8196. Ce n'est toujours pas correct car nous passons une colonne au lieu d'une chaîne littérale à la fonction.
Quelques ajustements au code de fonction seraient nécessaires pour donner une estimation plus précise dans ce cas particulier, mais je pense que le concept global est assez bien expliqué ici.
La fonction str2tbl utilisée dans cette réponse a été initialement développée par Tom Kyte: https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:110612348061
Le concept d'association de statistiques avec des types d'objets peut être exploré plus en détail en lisant cet article: http://www.oracle-developer.net/display.php?id=427
La technique décrite ici fonctionne en 10g +.
la source
REGEXP_COUNT n'a été ajouté qu'après Oracle 11i. Voici une solution Oracle 10g, adoptée à partir de la solution d'Art.
la source
À partir d'Oracle 12c, vous pouvez utiliser
JSON_TABLE
etJSON_ARRAY
:Et requête:
Production:
démo db <> fiddle
la source
Voici une implémentation alternative utilisant XMLTABLE qui permet la conversion en différents types de données:
... ou si vos chaînes délimitées sont stockées dans une ou plusieurs lignes d'une table:
la source
J'aimerais ajouter une autre méthode. Celui-ci utilise des requêtes récursives, ce que je n'ai pas vu dans les autres réponses. Il est pris en charge par Oracle depuis 11gR2.
Il est assez flexible avec le caractère de division. Changez-le simplement dans les
INSTR
appels.la source
Sans utiliser connect by ou regexp :
la source
J'ai eu le même problème et xmltable m'a aidé:
SELECT id, trim (COLUMN_VALUE) text FROM t, xmltable (('"' || REPLACE (text, ',', '", "') || '"'))
la source
Dans Oracle 11g et versions ultérieures, vous pouvez utiliser une sous-requête récursive et des fonctions de chaîne simples (qui peuvent être plus rapides que les expressions régulières et les sous-requêtes hiérarchiques corrélées):
Configuration d'Oracle :
Requête :
Sortie :
db <> violon ici
la source
J'avais utilisé la fonction DBMS_UTILITY.comma_to _table en fait son fonctionnement le code comme suit
j'avais utilisé mes propres noms de table et de colonne
la source
comma_to_table()
ne fonctionne qu'avec des jetons qui correspondent aux conventions de dénomination des objets de base de données Oracle. Il se lancera sur une corde comme'123,456,789'
par exemple.