Oracle'da dizeyi birden çok satıra bölme


104

Bunun PHP ve MYSQL ile bir dereceye kadar yanıtlandığını biliyorum, ancak birinin Oracle 10g (tercihen) ve 11g'de bir dizeyi (virgülle sınırlanmış) birden çok satıra bölmenin en basit yaklaşımını öğretip öğretemeyeceğini merak ediyordum.

Tablo aşağıdaki gibidir:

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

Aşağıdakileri oluşturmak istiyorum:

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

Yığın etrafında birkaç olası çözüm gördüm, ancak bunlar yalnızca tek bir sütunu (virgülle ayrılmış dize olarak) açıkladılar. Herhangi bir yardım çok takdir edilecektir.


2
Kullanarak örnekler için REGEXP, XMLTABLEve MODELfıkra, bkz Oracle SQL kullanarak bir tabloya Bölünmüş virgülle ayrılmış dizeleri
Lalit Kumar B

Yanıtlar:


121

Bu iyileştirilmiş bir yol olabilir (ayrıca regexp ve connect by ile):

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

DÜZENLEME : İşte sorgunun basit ("derinlemesine değil") açıklaması.

  1. length (regexp_replace(t.error, '[^,]+')) + 1kullanımları regexp_replacesınırlayıcı (bu durumda virgül) değil bir şey silmek ve length +1birçok elementler (hatalar) vardır nasıl gidilir.
  2. select level from dual connect by level <= (...)Bir kullanan hiyerarşik bir sorgu ile eşleşir artan sayıda hata sayısı 1 ile, bulunan bir sütun oluşturmak için.

    Ön izleme:

    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)) bazı oracle türlerinin dökümünü yapar.
    • cast(multiset(.....)) as sys.OdciNumberListSayıların Tek bir koleksiyon, OdciNumberList içine dönüşümler birden koleksiyonları (orijinal veri kümesindeki her satır için bir toplama).
    • table()Fonksiyon Bir sonuç içine bir koleksiyon dönüştürür.
  4. FROMbirleşimsiz veri kümeniz ve çoklu kümeniz arasında bir çapraz birleşim oluşturur . Sonuç olarak, veri kümesindeki 4 eşleşmeli bir satır 4 kez tekrarlanacaktır ("sütun_değer" adlı sütunda artan bir sayı ile).

    Ön izleme:

    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))column_valueiçin nth_appearance / ocurrence parametresi olarak kullanır regexp_substr.
  6. t.name, t.projectKolay görselleştirme için veri kümenizden ( örnek olarak) başka sütunlar ekleyebilirsiniz .

Oracle dokümanlarına bazı referanslar:


7
Dikkat! '[^,]+'Listede boş bir öğe varsa, dizeleri ayrıştırmak için biçimin bir normal ifadesi doğru öğeyi döndürmez. Daha fazla bilgi için buraya bakın: stackoverflow.com/questions/31464275/…
Gary_W

13
11g'den beri regexp_count(t.error, ',')bunun yerine kullanabilirsiniz length (regexp_replace(t.error, '[^,]+')), bu da başka bir performans iyileştirmesi sağlayabilir
Štefan Oravec

1
"Normal" BAĞLANTIYLA 485 saniye. 0,296 saniye bu şekilde. Harikasın! Şimdi tek yapmam gereken, nasıl çalıştığını anlamak. :-)
Bob Jarvis - Monica'yı yeniden

@BobJarvis, ne yaptığını açıklamak için bir düzenleme ekledi. Yazım / dilbilgisi düzeltmelerine açığız.
Nefreo

"Kabul edilen yanıtın performansı düşük" - bu başlıkta kabul edilen yanıt nedir? Lütfen diğer gönderiye referans vermek için bağlantıları kullanın.
0xdb

28

normal ifadeler harika bir şey :)

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

1
merhaba, sorguda farklı anahtar kelime kullanmadıysam yukarıdaki sorgunun neden yinelenen satırlar verdiğini
açıklar mısınız

2
Bu sorgu, özellikle büyük tablolarda @JagadeeshG nedeniyle kullanılamaz.
Michael-O

3
Son derece yavaş, aşağıda daha iyi bir cevap var
MoreCoffee

Yavaşlığın nedeni, her Names kombinasyonunun birbirine bağlı olmasıdır, bu da kaldırırsanız görülebilir distinct. Maalesef ekleyerek and Name = prior Nameiçin connect byfıkra nedenleri ORA-01436: CONNECT BY loop in user data.
mik

ORA-01436Ekleyerek AND name = PRIOR name(veya birincil anahtar ne olursa olsun) hatayı önleyebilirsiniz ve AND PRIOR SYS_GUID() IS NOT NULL
David Faber

28

Aşağıdaki ikisi arasında çok büyük bir fark var:

  • tek bir sınırlandırılmış dizeyi bölme
  • bir tablodaki birden çok satır için sınırlandırılmış dizeleri bölme.

Satırları kısıtlamazsanız, CONNECT BY yan tümcesi birden çok satır üretir ve istenen çıktıyı vermez.

Normal İfadeler dışında , birkaç başka alternatif de kullanılıyor:

  • XMLTable
  • MODEL maddesi

Kurmak

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>

XMLTABLE kullanarak :

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>

MODEL yan tümcesini kullanma :

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>

1
Daha fazla ayrıntıya ('"' || REPLACE(text, ',', '","') || '"')girebilir misiniz, neden olmalı ve parantezler kaldırılamaz? Oracle dokümanları ([ docs.oracle.com/database/121/SQLRF/functions268.htm ) benim için açık değil. Öyle mi XQuery_string?
Betlista

@Betlista bir XQuery ifadesidir.
Lalit Kumar B

XMLTABLE çözümü bazı nedenlerden dolayı, karışık uzunluktaki satırlar için son girdiyi çıktı olarak vermede sürekli başarısız oluyor. Örneğin. satır1: 3 kelime; satır2: 2 kelime, satır3: 1 kelime; satır4: 2 kelime, satır5: 1 kelime - son kelimeyi çıkarmaz. Satırların sıralanması önemli değil.
Gnudiff

8

Aynı şeyden birkaç örnek daha:

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
/

Ayrıca DBMS_UTILITY.comma_to_table & table_to_comma kullanabilirsiniz: http://www.oracle-base.com/articles/9i/useful-procedures-and-functions-9i.php#DBMS_UTILITY.comma_to_table


Unutmayın comma_to_table()sadece Oracle'ın veritabanı nesnesi adlandırma kurallarına uyması jeton ile çalışır. '123,456,789'Örneğin bir ipte fırlayacak .
APC

7

BORU HATLI tablo işlevi kullanarak farklı bir yaklaşım önermek istiyorum. Karakter dizesini bölmek için kendi özel işlevinizi sağlamanız dışında, XMLTABLE tekniğine biraz benzer:

-- 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));

Sonuçlar:

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

Bu tür bir yaklaşımla ilgili sorun, çoğu zaman optimize edicinin tablo işlevinin önemini bilmemesi ve bir tahmin yapması gerekmesidir. Bu, yürütme planlarınız için potansiyel olarak zararlı olabilir, bu nedenle bu çözüm, optimize edici için yürütme istatistikleri sağlayacak şekilde genişletilebilir.

Bu optimize edici tahminini yukarıdaki sorguda bir AÇIKLAMA PLANI çalıştırarak görebilirsiniz:

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 |
----------------------------------------------------------------------------------------------

Koleksiyon yalnızca 3 değere sahip olsa da, iyileştirici bunun için 8168 satır tahmin etti (varsayılan değer). Bu ilk bakışta alakasız görünebilir, ancak optimize edicinin optimalin altında bir plana karar vermesi yeterli olabilir.

Çözüm, koleksiyona istatistik sağlamak için optimize edici uzantılarını kullanmaktır:

-- 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;

Ortaya çıkan yürütme planını test etmek:

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 |
----------------------------------------------------------------------------------------------

Gördüğünüz gibi yukarıdaki plandaki önem düzeyi artık 8196'nın tahmin edilen değeri değil. Hala doğru değil çünkü işleve bir dizge yerine bir sütun geçiriyoruz.

Bu özel durumda daha yakın bir tahminde bulunmak için işlev kodunda biraz ince ayar yapılması gerekebilir, ancak genel konseptin burada hemen hemen açıklandığını düşünüyorum.

Bu cevapta kullanılan str2tbl işlevi orijinal olarak Tom Kyte tarafından geliştirilmiştir: https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:110612348061

İstatistikleri nesne türleriyle ilişkilendirme kavramı, bu makaleyi okuyarak daha ayrıntılı incelenebilir: http://www.oracle-developer.net/display.php?id=427

Burada açıklanan teknik 10g + olarak çalışır.


4

REGEXP_COUNT, Oracle 11i'ye kadar eklenmedi. İşte Art'ın çözümünden uyarlanan bir Oracle 10g çözümü.

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;

Bunun için nasıl filtre ekleyebilirim, sadece name = '108' ile filtrelemek istediğimi söyleyelim. From cümlesinden sonra bir where eklemeyi denedim, ancak yinelemelerle sonuçlandım.
DRTauli

4

Oracle Kullanabileceğin 12c başlayarak JSON_TABLEve JSON_ARRAY:

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

Ve sorgu:

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;

Çıktı:

┌──────┬─────────┬──────────────────┬──────┐
 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 <> fiddle demosu


1
Bunun akıllıca bir numara olduğunu kabul ediyorum, ancak açıkçası bir kod tabanında karşılaşırsam beni şaşırtacaktı.
APC

@APC Bu sadece SQL ile nelerin mümkün olduğunu gösterir. Kod
tabanımda

Elbette. Bu iş parçacığı, Oracle ile dizi belirteçleri için en popüler hitlerden biri, bu yüzden masumları kendilerinden korumak için daha egzotik çözümlere uyarılar eklememiz gerektiğini düşünüyorum :)
APC

3

Farklı veri türlerine çevrim yapılmasına izin veren XMLTABLE kullanan alternatif bir uygulama:

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

... veya sınırlandırılmış dizeleriniz bir tablonun bir veya daha fazla satırında saklanıyorsa:

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
;

Bu çözümün Oracle 11.2.0.3 ve sonraki sürümler için çalıştığını düşünüyorum.
APC

2

Başka bir yöntem eklemek istiyorum. Bu, diğer cevaplarda görmediğim özyinelemeli sorguları kullanıyor. Oracle tarafından 11gR2'den beri desteklenmektedir.

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

Bölme karakteri ile oldukça esnektir. Sadece INSTRaramalarda değiştirin .


2

Kullanmadan göre bağlantı ya da düzenli ifadesi :

    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;

1

Aynı sorunu yaşadım ve xmltable bana yardımcı oldu:

ID SEÇ, t'den (COLUMN_VALUE) metni kırp, xmltable (('"' || DEĞİŞTİR (metin, ',', '", "') || '"'))


0

Oracle 11g ve sonraki sürümlerde, özyinelemeli bir alt sorgu ve basit dize işlevlerini (normal ifadelerden ve ilişkili hiyerarşik alt sorgulardan daha hızlı olabilir) kullanabilirsiniz:

Oracle Kurulumu :

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

Sorgu :

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

Çıktı :

İSİM | PROJE | HATA
---: | : ------ | : ----
 108 | test | Err1
 109 | test2 | Err1
 108 | test | Err2
 108 | test | Err3

db <> burada fiddle


-1

DBMS_UTILITY.comma_to _table işlevini kullandım aslında kodu aşağıdaki gibi çalışıyor

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; 

kendi tablo ve sütun isimlerimi kullanmıştım


5
Unutmayın comma_to_table()sadece Oracle'ın veritabanı nesnesi adlandırma kurallarına uyması jeton ile çalışır. '123,456,789'Örneğin bir ipte fırlayacak .
APC

geçici tablolar kullanarak uygulayabilir miyiz?
Smart003

1
Umm, diğer tüm uygulanabilir çözümler göz önüne alındığında, neden verileri gerçekleştirmenin devasa bir ek yükü olan geçici tabloları kullanmak isteyelim?
APC
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.