Aufteilen von Zeichenfolgen in mehrere Zeilen in Oracle

104

Ich weiß, dass dies bis zu einem gewissen Grad mit PHP und MYSQL beantwortet wurde, aber ich habe mich gefragt, ob mir jemand den einfachsten Ansatz zum Aufteilen einer Zeichenfolge (durch Kommas getrennt) in mehrere Zeilen in Oracle 10g (vorzugsweise) und 11g beibringen kann.

Die Tabelle lautet wie folgt:

Name | Project | Error 
108    test      Err1, Err2, Err3
109    test2     Err1

Ich möchte Folgendes erstellen:

Name | Project | Error
108    Test      Err1
108    Test      Err2 
108    Test      Err3 
109    Test2     Err1

Ich habe einige mögliche Lösungen rund um den Stapel gesehen, die jedoch nur eine einzige Spalte ausmachten (die durch Kommas getrennte Zeichenfolge). Jede Hilfe wäre sehr dankbar.

Marshalllaw
quelle
2
Für Beispiele mit REGEXP, XMLTABLEund MODELKlausel findet Split Komma getrennte Zeichenfolge in einer Tabelle mit Oracle SQL
Lalit Kumar B

Antworten:

121

Dies kann ein verbesserter Weg sein (auch mit Regexp und Connect by):

with temp as
(
    select 108 Name, 'test' Project, 'Err1, Err2, Err3' Error  from dual
    union all
    select 109, 'test2', 'Err1' from dual
)
select distinct
  t.name, t.project,
  trim(regexp_substr(t.error, '[^,]+', 1, levels.column_value))  as error
from 
  temp t,
  table(cast(multiset(select level from dual connect by  level <= length (regexp_replace(t.error, '[^,]+'))  + 1) as sys.OdciNumberList)) levels
order by name

BEARBEITEN : Hier ist eine einfache (wie in "nicht in der Tiefe") Erklärung der Abfrage.

  1. length (regexp_replace(t.error, '[^,]+')) + 1wird verwendet regexp_replace, um alles zu löschen, was nicht das Trennzeichen ist (in diesem Fall Komma), und um length +1zu ermitteln, wie viele Elemente (Fehler) vorhanden sind.
  2. Der select level from dual connect by level <= (...)verwendet eine hierarchische Abfrage , um eine Spalte mit einer zunehmenden Anzahl gefundener Übereinstimmungen von 1 bis zur Gesamtzahl der Fehler zu erstellen.

    Vorschau:

    select level, length (regexp_replace('Err1, Err2, Err3', '[^,]+'))  + 1 as max 
    from dual connect by level <= length (regexp_replace('Err1, Err2, Err3', '[^,]+'))  + 1
  3. table(cast(multiset(.....) as sys.OdciNumberList)) macht einige Casting von Orakeltypen.
    • Das cast(multiset(.....)) as sys.OdciNumberListtransformiert mehrere Sammlungen (eine Sammlung für jede Zeile im Originaldatensatz) in eine einzige Sammlung von Zahlen, OdciNumberList.
    • Die table()Funktion wandelt eine Sammlung in eine Ergebnismenge um.
  4. FROMOhne Join wird ein Cross-Join zwischen Ihrem Dataset und dem Multiset erstellt. Infolgedessen wird eine Zeile im Datensatz mit 4 Übereinstimmungen viermal wiederholt (mit einer zunehmenden Zahl in der Spalte "column_value").

    Vorschau:

    select * from 
    temp t,
    table(cast(multiset(select level from dual connect by  level <= length (regexp_replace(t.error, '[^,]+'))  + 1) as sys.OdciNumberList)) levels
  5. trim(regexp_substr(t.error, '[^,]+', 1, levels.column_value))verwendet den Parameter column_valueals nth_appearance / ocurrence für regexp_substr.
  6. Sie können einige andere Spalten aus Ihrem Datensatz hinzufügen ( t.name, t.projectals Beispiel), um die Visualisierung zu vereinfachen.

Einige Verweise auf Oracle-Dokumente:

Nefreo
quelle
7
In acht nehmen! Ein regulärer Ausdruck des Formats '[^,]+'zum Analysieren von Zeichenfolgen gibt nicht das richtige Element zurück, wenn die Liste ein Nullelement enthält. Weitere Informationen finden Sie hier: stackoverflow.com/questions/31464275/…
Gary_W
13
seit 11g können Sie regexp_count(t.error, ',')anstelle von verwenden length (regexp_replace(t.error, '[^,]+')), was eine weitere Leistungsverbesserung bringen kann
Štefan Oravec
1
485 Sekunden mit "normal" CONNECT BY. 0,296 Sekunden auf diese Weise. Du ROCK! Jetzt muss ich nur noch verstehen, wie es funktioniert. :-)
Bob Jarvis - Reinstate Monica
@ BobJarvis hat eine Bearbeitung hinzugefügt, um zu erklären, was es tut. Rechtschreib- / Grammatikkorrekturen sind willkommen.
Nefreo
"Die akzeptierte Antwort hat eine schlechte Leistung" - wie lautet die akzeptierte Antwort in diesem Thema? Bitte benutzen Sie die Links, um auf den anderen Beitrag zu verweisen.
0xdb
28

reguläre Ausdrücke sind eine wunderbare Sache :)

with temp as  (
       select 108 Name, 'test' Project, 'Err1, Err2, Err3' Error  from dual
       union all
       select 109, 'test2', 'Err1' from dual
     )

SELECT distinct Name, Project, trim(regexp_substr(str, '[^,]+', 1, level)) str
  FROM (SELECT Name, Project, Error str FROM temp) t
CONNECT BY instr(str, ',', 1, level - 1) > 0
order by Name
Andrey Khmelev
quelle
1
Hallo, können Sie mir bitte klarstellen, warum die obige Abfrage doppelte Zeilen enthält, wenn ich in der Abfrage kein eindeutiges Schlüsselwort verwendet habe
Jagadeesh G
2
Diese Abfrage ist aufgrund von @JagadeeshG unbrauchbar, insbesondere bei großen Tabellen.
Michael-O
3
Extrem langsam, es gibt eine bessere Antwort unten
MoreCoffee
Der Grund für die Langsamkeit ist, dass jede Kombination von Names verbunden ist, was sichtbar wird, wenn Sie entfernen distinct. Leider fügt and Name = prior Namedas Hinzufügen zur connect byKlausel Ursachen hinzu ORA-01436: CONNECT BY loop in user data.
Mik
Sie können den ORA-01436Fehler vermeiden, indem Sie AND name = PRIOR name(oder was auch immer der Primärschlüssel sein mag) und AND PRIOR SYS_GUID() IS NOT NULL
David Faber
28

Es gibt einen großen Unterschied zwischen den beiden folgenden:

  • Teilen einer einzelnen begrenzten Zeichenfolge
  • Teilen von getrennten Zeichenfolgen für mehrere Zeilen in einer Tabelle.

Wenn Sie die Zeilen nicht einschränken, erzeugt die CONNECT BY- Klausel mehrere Zeilen und liefert nicht die gewünschte Ausgabe.

Neben regulären Ausdrücken werden einige andere Alternativen verwendet:

  • XMLTable
  • MODEL- Klausel

Konfiguration

SQL> CREATE TABLE t (
  2    ID          NUMBER GENERATED ALWAYS AS IDENTITY,
  3    text        VARCHAR2(100)
  4  );

Table created.

SQL>
SQL> INSERT INTO t (text) VALUES ('word1, word2, word3');

1 row created.

SQL> INSERT INTO t (text) VALUES ('word4, word5, word6');

1 row created.

SQL> INSERT INTO t (text) VALUES ('word7, word8, word9');

1 row created.

SQL> COMMIT;

Commit complete.

SQL>
SQL> SELECT * FROM t;

        ID TEXT
---------- ----------------------------------------------
         1 word1, word2, word3
         2 word4, word5, word6
         3 word7, word8, word9

SQL>

Verwenden von XMLTABLE :

SQL> SELECT id,
  2         trim(COLUMN_VALUE) text
  3  FROM t,
  4    xmltable(('"'
  5    || REPLACE(text, ',', '","')
  6    || '"'))
  7  /

        ID TEXT
---------- ------------------------
         1 word1
         1 word2
         1 word3
         2 word4
         2 word5
         2 word6
         3 word7
         3 word8
         3 word9

9 rows selected.

SQL>

Verwenden der MODEL- Klausel:

SQL> WITH
  2  model_param AS
  3     (
  4            SELECT id,
  5                      text AS orig_str ,
  6                   ','
  7                          || text
  8                          || ','                                 AS mod_str ,
  9                   1                                             AS start_pos ,
 10                   Length(text)                                   AS end_pos ,
 11                   (Length(text) - Length(Replace(text, ','))) + 1 AS element_count ,
 12                   0                                             AS element_no ,
 13                   ROWNUM                                        AS rn
 14            FROM   t )
 15     SELECT   id,
 16              trim(Substr(mod_str, start_pos, end_pos-start_pos)) text
 17     FROM     (
 18                     SELECT *
 19                     FROM   model_param MODEL PARTITION BY (id, rn, orig_str, mod_str)
 20                     DIMENSION BY (element_no)
 21                     MEASURES (start_pos, end_pos, element_count)
 22                     RULES ITERATE (2000)
 23                     UNTIL (ITERATION_NUMBER+1 = element_count[0])
 24                     ( start_pos[ITERATION_NUMBER+1] = instr(cv(mod_str), ',', 1, cv(element_no)) + 1,
 25                     end_pos[iteration_number+1] = instr(cv(mod_str), ',', 1, cv(element_no) + 1) )
 26                 )
 27     WHERE    element_no != 0
 28     ORDER BY mod_str ,
 29           element_no
 30  /

        ID TEXT
---------- --------------------------------------------------
         1 word1
         1 word2
         1 word3
         2 word4
         2 word5
         2 word6
         3 word7
         3 word8
         3 word9

9 rows selected.

SQL>
Lalit Kumar B.
quelle
1
Können Sie näher erläutern, warum es ('"' || REPLACE(text, ',', '","') || '"')Klammern geben muss und nicht entfernt werden können? Oracle-Dokumente ([ docs.oracle.com/database/121/SQLRF/functions268.htm ) sind mir nicht klar. Ist es XQuery_string?
Betlista
@Betlista ist ein XQuery-Ausdruck.
Lalit Kumar B
Die XMLTABLE-Lösung gibt aus irgendeinem Grund nicht immer den letzten Eintrag für Zeilen gemischter Länge aus. Z.B. Zeile 1: 3 Wörter; Zeile 2: 2 Wörter, Zeile 3: 1 Wort; Zeile 4: 2 Wörter, Zeile 5: 1 Wort - gibt nicht das letzte Wort aus. Die Reihenfolge der Zeilen spielt keine Rolle.
Gnudiff
7

Ein paar weitere Beispiele dafür:

SELECT trim(regexp_substr('Err1, Err2, Err3', '[^,]+', 1, LEVEL)) str_2_tab
  FROM dual
CONNECT BY LEVEL <= regexp_count('Err1, Err2, Err3', ',')+1
/

SELECT trim(regexp_substr('Err1, Err2, Err3', '[^,]+', 1, LEVEL)) str_2_tab
  FROM dual
CONNECT BY LEVEL <= length('Err1, Err2, Err3') - length(REPLACE('Err1, Err2, Err3', ',', ''))+1
/

Verwenden Sie möglicherweise auch 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

Kunst
quelle
Beachten Sie, dass dies comma_to_table()nur mit Token funktioniert, die den Namenskonventionen für Datenbankobjekte von Oracle entsprechen. Es wird wie '123,456,789'zum Beispiel auf eine Schnur geschleudert.
APC
7

Ich möchte einen anderen Ansatz mit einer PIPELINED-Tabellenfunktion vorschlagen. Es ähnelt etwas der Technik von XMLTABLE, außer dass Sie Ihre eigene benutzerdefinierte Funktion zum Teilen der Zeichenfolge bereitstellen:

-- Create a collection type to hold the results
CREATE OR REPLACE TYPE typ_str2tbl_nst AS TABLE OF VARCHAR2(30);
/

-- Split the string according to the specified delimiter
CREATE OR REPLACE FUNCTION str2tbl (
  p_string    VARCHAR2,
  p_delimiter CHAR DEFAULT ',' 
)
RETURN typ_str2tbl_nst PIPELINED
AS
  l_tmp VARCHAR2(32000) := p_string || p_delimiter;
  l_pos NUMBER;
BEGIN
  LOOP
    l_pos := INSTR( l_tmp, p_delimiter );
    EXIT WHEN NVL( l_pos, 0 ) = 0;
    PIPE ROW ( RTRIM( LTRIM( SUBSTR( l_tmp, 1, l_pos-1) ) ) );
    l_tmp := SUBSTR( l_tmp, l_pos+1 );
  END LOOP;
END str2tbl;
/

-- The problem solution
SELECT name, 
       project, 
       TRIM(COLUMN_VALUE) error
  FROM t, TABLE(str2tbl(error));

Ergebnisse:

      NAME PROJECT    ERROR
---------- ---------- --------------------
       108 test       Err1
       108 test       Err2
       108 test       Err3
       109 test2      Err1

Das Problem bei dieser Art von Ansatz ist, dass der Optimierer häufig die Kardinalität der Tabellenfunktion nicht kennt und eine Vermutung anstellen muss. Dies kann möglicherweise schädlich für Ihre Ausführungspläne sein. Daher kann diese Lösung erweitert werden, um Ausführungsstatistiken für das Optimierungsprogramm bereitzustellen.

Sie können diese Optimierungsschätzung anzeigen, indem Sie einen EXPLAIN PLAN für die obige Abfrage ausführen:

Execution Plan
----------------------------------------------------------
Plan hash value: 2402555806

----------------------------------------------------------------------------------------------
| Id  | Operation                          | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                   |         | 16336 |   366K|    59   (0)| 00:00:01 |
|   1 |  NESTED LOOPS                      |         | 16336 |   366K|    59   (0)| 00:00:01 |
|   2 |   TABLE ACCESS FULL                | T       |     2 |    42 |     3   (0)| 00:00:01 |
|   3 |   COLLECTION ITERATOR PICKLER FETCH| STR2TBL |  8168 | 16336 |    28   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------

Obwohl die Sammlung nur 3 Werte enthält, hat der Optimierer 8168 Zeilen dafür geschätzt (Standardwert). Dies mag zunächst irrelevant erscheinen, aber es kann für den Optimierer ausreichend sein, sich für einen suboptimalen Plan zu entscheiden.

Die Lösung besteht darin, die Optimierungserweiterungen zu verwenden, um Statistiken für die Sammlung bereitzustellen:

-- Create the optimizer interface to the str2tbl function
CREATE OR REPLACE TYPE typ_str2tbl_stats AS OBJECT (
  dummy NUMBER,

  STATIC FUNCTION ODCIGetInterfaces ( p_interfaces OUT SYS.ODCIObjectList )
  RETURN NUMBER,

  STATIC FUNCTION ODCIStatsTableFunction ( p_function  IN  SYS.ODCIFuncInfo,
                                           p_stats     OUT SYS.ODCITabFuncStats,
                                           p_args      IN  SYS.ODCIArgDescList,
                                           p_string    IN  VARCHAR2,
                                           p_delimiter IN  CHAR DEFAULT ',' )
  RETURN NUMBER
);
/

-- Optimizer interface implementation
CREATE OR REPLACE TYPE BODY typ_str2tbl_stats
AS
  STATIC FUNCTION ODCIGetInterfaces ( p_interfaces OUT SYS.ODCIObjectList )
  RETURN NUMBER
  AS
  BEGIN
    p_interfaces := SYS.ODCIObjectList ( SYS.ODCIObject ('SYS', 'ODCISTATS2') );
    RETURN ODCIConst.SUCCESS;
  END ODCIGetInterfaces;

  -- This function is responsible for returning the cardinality estimate
  STATIC FUNCTION ODCIStatsTableFunction ( p_function  IN  SYS.ODCIFuncInfo,
                                           p_stats     OUT SYS.ODCITabFuncStats,
                                           p_args      IN  SYS.ODCIArgDescList,
                                           p_string    IN  VARCHAR2,
                                           p_delimiter IN  CHAR DEFAULT ',' )
  RETURN NUMBER
  AS
  BEGIN
    -- I'm using basically half the string lenght as an estimator for its cardinality
    p_stats := SYS.ODCITabFuncStats( CEIL( LENGTH( p_string ) / 2 ) );
    RETURN ODCIConst.SUCCESS;
  END ODCIStatsTableFunction;

END;
/

-- Associate our optimizer extension with the PIPELINED function   
ASSOCIATE STATISTICS WITH FUNCTIONS str2tbl USING typ_str2tbl_stats;

Testen des resultierenden Ausführungsplans:

Execution Plan
----------------------------------------------------------
Plan hash value: 2402555806

----------------------------------------------------------------------------------------------
| Id  | Operation                          | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                   |         |     1 |    23 |    59   (0)| 00:00:01 |
|   1 |  NESTED LOOPS                      |         |     1 |    23 |    59   (0)| 00:00:01 |
|   2 |   TABLE ACCESS FULL                | T       |     2 |    42 |     3   (0)| 00:00:01 |
|   3 |   COLLECTION ITERATOR PICKLER FETCH| STR2TBL |     1 |     2 |    28   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------

Wie Sie sehen können, ist die Kardinalität im obigen Plan nicht mehr der geschätzte Wert von 8196. Es ist immer noch nicht korrekt, da wir eine Spalte anstelle eines Zeichenfolgenliteral an die Funktion übergeben.

In diesem speziellen Fall wäre eine gewisse Anpassung des Funktionscodes erforderlich, um eine genauere Schätzung vorzunehmen, aber ich denke, das Gesamtkonzept wird hier ziemlich genau erläutert.

Die in dieser Antwort verwendete str2tbl-Funktion wurde ursprünglich von Tom Kyte entwickelt: https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:110612348061

Das Konzept der Zuordnung von Statistiken zu Objekttypen kann in diesem Artikel näher erläutert werden: http://www.oracle-developer.net/display.php?id=427

Die hier beschriebene Technik funktioniert in 10g +.

Daniela Petruzalek
quelle
4

REGEXP_COUNT wurde erst in Oracle 11i hinzugefügt. Hier ist eine Oracle 10g-Lösung, die aus der Art-Lösung übernommen wurde.

SELECT trim(regexp_substr('Err1, Err2, Err3', '[^,]+', 1, LEVEL)) str_2_tab
  FROM dual
CONNECT BY LEVEL <=
  LENGTH('Err1, Err2, Err3')
    - LENGTH(REPLACE('Err1, Err2, Err3', ',', ''))
    + 1;
Durette
quelle
Wie kann ich einen Filter hinzufügen? Angenommen, ich möchte nur mit name = '108' filtern. Ich habe versucht, ein where nach der from-Klausel hinzuzufügen, habe aber Duplikate erhalten.
DRTauli
4

Ausgehend von Oracle 12c Sie nutzen könnten JSON_TABLEund JSON_ARRAY:

CREATE TABLE tab(Name, Project, Error) AS
SELECT 108,'test' ,'Err1, Err2, Err3' FROM dual UNION 
SELECT 109,'test2','Err1'             FROM dual;

Und fragen Sie:

SELECT *
FROM tab t
OUTER APPLY (SELECT TRIM(p) AS p
            FROM JSON_TABLE(REPLACE(JSON_ARRAY(t.Error), ',', '","'),
           '$[*]' COLUMNS (p VARCHAR2(4000) PATH '$'))) s;

Ausgabe:

┌──────┬─────────┬──────────────────┬──────┐
 Name  Project       Error         P   
├──────┼─────────┼──────────────────┼──────┤
  108  test     Err1, Err2, Err3  Err1 
  108  test     Err1, Err2, Err3  Err2 
  108  test     Err1, Err2, Err3  Err3 
  109  test2    Err1              Err1 
└──────┴─────────┴──────────────────┴──────┘

db <> Geigen-Demo

Lukasz Szozda
quelle
1
Ich gebe zu, dass dies ein kluger Trick ist, aber ehrlich gesagt würde es mich verwirren, wenn ich in einer Codebasis darauf stoßen würde.
APC
@APC Dies ist nur eine Demonstration dessen, was mit SQL möglich ist. Wenn ich solchen Code in meiner Codebasis verwenden muss, würde ich ihn definitiv in eine Funktion
einschließen
Natürlich. Es ist nur so, dass dieser Thread einer der beliebtesten Hits für die String-Tokenisierung mit Oracle ist. Ich denke, wir sollten Vorbehalte gegen die exotischeren Lösungen einschließen, um die Unschuldigen vor sich selbst zu schützen :)
APC
3

Hier ist eine alternative Implementierung mit XMLTABLE, die das Casting in verschiedene Datentypen ermöglicht:

select 
  xmltab.txt
from xmltable(
  'for $text in tokenize("a,b,c", ",") return $text'
  columns 
    txt varchar2(4000) path '.'
) xmltab
;

... oder wenn Ihre durch Trennzeichen getrennten Zeichenfolgen in einer oder mehreren Zeilen einer Tabelle gespeichert sind:

select 
  xmltab.txt
from (
  select 'a;b;c' inpt from dual union all
  select 'd;e;f' from dual
) base
inner join xmltable(
  'for $text in tokenize($input, ";") return $text'
  passing base.inpt as "input"
  columns 
    txt varchar2(4000) path '.'
) xmltab
  on 1=1
;
Silentsurfer
quelle
Ich denke, diese Lösung funktioniert für Oracle 11.2.0.3 und spätere Versionen.
APC
2

Ich möchte eine andere Methode hinzufügen. Dieser verwendet rekursive Abfragen, was ich in den anderen Antworten nicht gesehen habe. Es wird von Oracle seit 11gR2 unterstützt.

with cte0 as (
    select phone_number x
    from hr.employees
), cte1(xstr,xrest,xremoved) as (
        select x, x, null
        from cte0
    union all        
        select xstr,
            case when instr(xrest,'.') = 0 then null else substr(xrest,instr(xrest,'.')+1) end,
            case when instr(xrest,'.') = 0 then xrest else substr(xrest,1,instr(xrest,'.') - 1) end
        from cte1
        where xrest is not null
)
select xstr, xremoved from cte1  
where xremoved is not null
order by xstr

Es ist sehr flexibel mit dem spaltenden Charakter. Ändern Sie es einfach in den INSTRAnrufen.

Thomas Tschernich
quelle
2

Ohne Connect by oder Regexp zu verwenden :

    with mytable as (
      select 108 name, 'test' project, 'Err1,Err2,Err3' error from dual
      union all
      select 109, 'test2', 'Err1' from dual
    )
    ,x as (
      select name
      ,project
      ,','||error||',' error
      from mytable
    )
    ,iter as (SELECT rownum AS pos
        FROM all_objects
    )
    select x.name,x.project
    ,SUBSTR(x.error
      ,INSTR(x.error, ',', 1, iter.pos) + 1
      ,INSTR(x.error, ',', 1, iter.pos + 1)-INSTR(x.error, ',', 1, iter.pos)-1
    ) error
    from x, iter
    where iter.pos < = (LENGTH(x.error) - LENGTH(REPLACE(x.error, ','))) - 1;
Ilya Kharlamov
quelle
1

Ich hatte das gleiche Problem und xmltable half mir:

SELECT id, trim (COLUMN_VALUE) Text FROM t, xmltable (('"' || REPLACE (text, ',', '", "') || '"'))

Volkov Maxim
quelle
0

In Oracle 11g und höher können Sie eine rekursive Unterabfrage und einfache Zeichenfolgenfunktionen verwenden (die möglicherweise schneller sind als reguläre Ausdrücke und korrelierte hierarchische Unterabfragen):

Oracle Setup :

CREATE TABLE table_name ( name, project, error ) as
 select 108, 'test',  'Err1, Err2, Err3' from dual union all
 select 109, 'test2', 'Err1'             from dual;

Abfrage :

WITH table_name_error_bounds ( name, project, error, start_pos, end_pos ) AS (
  SELECT name,
         project,
         error,
         1,
         INSTR( error, ', ', 1 )
  FROM   table_name
UNION ALL
  SELECT name,
         project,
         error,
         end_pos + 2,
         INSTR( error, ', ', end_pos + 2 )
  FROM   table_name_error_bounds
  WHERE  end_pos > 0
)
SELECT name,
       project,
       CASE end_pos
       WHEN 0
       THEN SUBSTR( error, start_pos )
       ELSE SUBSTR( error, start_pos, end_pos - start_pos )
       END AS error
FROM   table_name_error_bounds

Ausgabe :

NAME | PROJEKT | ERROR
---: | : ------ | : ----
 108 | test | Err1
 109 | test2 | Err1
 108 | test | Err2
 108 | test | Err3

db <> hier fummeln

MT0
quelle
-1

Ich hatte die Funktion DBMS_UTILITY.comma_to _table verwendet, um den Code wie folgt zu bearbeiten

declare
l_tablen  BINARY_INTEGER;
l_tab     DBMS_UTILITY.uncl_array;
cursor cur is select * from qwer;
rec cur%rowtype;
begin
open cur;
loop
fetch cur into rec;
exit when cur%notfound;
DBMS_UTILITY.comma_to_table (
     list   => rec.val,
     tablen => l_tablen,
     tab    => l_tab);
FOR i IN 1 .. l_tablen LOOP
    DBMS_OUTPUT.put_line(i || ' : ' || l_tab(i));
END LOOP;
end loop;
close cur;
end; 

Ich hatte meine eigenen Tabellen- und Spaltennamen verwendet

Smart003
quelle
5
Beachten Sie, dass dies comma_to_table()nur mit Token funktioniert, die den Namenskonventionen für Datenbankobjekte von Oracle entsprechen. Es wird wie '123,456,789'zum Beispiel auf eine Schnur geschleudert.
APC
können wir mit temporären Tabellen implementieren?
Smart003
1
Umm, angesichts all der anderen praktikablen Lösungen, warum sollten wir temporäre Tabellen verwenden wollen, die mit einem massiven Aufwand für die Materialisierung der Daten verbunden sind?
APC