Wiem, że do pewnego stopnia odpowiedziano na to za pomocą PHP i MYSQL, ale zastanawiałem się, czy ktoś mógłby mnie nauczyć najprostszego podejścia do dzielenia ciągu (rozdzielanego przecinkami) na wiele wierszy w Oracle 10g (najlepiej) i 11g.
Tabela przedstawia się następująco:
Name | Project | Error
108 test Err1, Err2, Err3
109 test2 Err1
Chcę utworzyć:
Name | Project | Error
108 Test Err1
108 Test Err2
108 Test Err3
109 Test2 Err1
Widziałem kilka potencjalnych rozwiązań dotyczących stosu, jednak dotyczyły one tylko jednej kolumny (będącej ciągiem rozdzielanym przecinkami). Każda pomoc byłaby bardzo mile widziana.
REGEXP
,XMLTABLE
orazMODEL
klauzuli zobaczyć Podział rozdzielany przecinkami ciągi w tabeli przy użyciu Oracle SQLOdpowiedzi:
To może być ulepszony sposób (również w przypadku wyrażenia regularnego i łączenia przez):
EDYCJA : Oto proste (jak w przypadku „nie dogłębnego”) wyjaśnienie zapytania.
length (regexp_replace(t.error, '[^,]+')) + 1
używaregexp_replace
do wymazania wszystkiego, co nie jest ogranicznikiem (w tym przypadku przecinka) ilength +1
do obliczenia liczby elementów (błędów).select level from dual connect by level <= (...)
Wykorzystuje hierarchicznego zapytania w celu utworzenia kolumny coraz odnalezione od 1 do całkowitej liczby błędów.Zapowiedź:
table(cast(multiset(.....) as sys.OdciNumberList))
wykonuje niektóre odlewania typów wyroczni.cast(multiset(.....)) as sys.OdciNumberList
transformat kilka zbiorów (jeden zbiór dla każdego rzędu w pierwotnym zestawie danych) w jeden zbiór cyfr, OdciNumberList.table()
Funkcja przekształca zbiór do wynikowego.FROM
bez sprzężenia tworzy sprzężenie krzyżowe między zbiorem danych a zestawem wielozbiorowym. W rezultacie wiersz w zestawie danych z 4 dopasowaniami powtórzy się 4 razy (z rosnącą liczbą w kolumnie o nazwie „wartość_kolumny”).Zapowiedź:
trim(regexp_substr(t.error, '[^,]+', 1, levels.column_value))
używacolumn_value
jako parametru nth_appearance / ocurrence dlaregexp_substr
.t.name, t.project
na przykład), aby ułatwić wizualizację.Niektóre odniesienia do dokumentów Oracle:
źródło
'[^,]+'
do analizowania ciągów nie zwraca prawidłowego elementu, jeśli na liście znajduje się element pusty. Zobacz tutaj, aby uzyskać więcej informacji: stackoverflow.com/questions/31464275/…regexp_count(t.error, ',')
zamiastlength (regexp_replace(t.error, '[^,]+'))
, co może przynieść kolejną poprawę wydajnościwyrażenia regularne to cudowna rzecz :)
źródło
Name
s jest połączona, co można zobaczyć, jeśli usunieszdistinct
. Niestety dodanieand Name = prior Name
doconnect by
klauzuli powodujeORA-01436: CONNECT BY loop in user data
.ORA-01436
błędu, dodającAND name = PRIOR name
(lub jakikolwiek inny klucz podstawowy) iAND PRIOR SYS_GUID() IS NOT NULL
Istnieje ogromna różnica między tymi dwoma:
Jeśli nie ograniczysz wierszy, klauzula CONNECT BY wygeneruje wiele wierszy i nie da pożądanego wyniku.
Oprócz wyrażeń regularnych używa się kilku innych alternatyw:
Ustawiać
Korzystanie z XMLTABLE :
Korzystanie z klauzuli MODEL :
źródło
('"' || REPLACE(text, ',', '","') || '"')
a nawiasów nie można usunąć? Dokumenty Oracle ([ docs.oracle.com/database/121/SQLRF/functions268.htm ) nie są dla mnie jasne. Czy to jestXQuery_string
?Kilka innych przykładów tego samego:
Można również użyć 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
źródło
comma_to_table()
działa tylko z tokenami, które pasują do konwencji nazewnictwa obiektów bazy danych Oracle. Rzuci się na strunę, jak'123,456,789'
na przykład.Chciałbym zaproponować inne podejście przy użyciu funkcji tabeli PIPELINED. Jest to trochę podobne do techniki XMLTABLE, z tym wyjątkiem, że udostępniasz własną niestandardową funkcję do dzielenia ciągu znaków:
Wyniki:
Problem z tego typu podejściem polega na tym, że często optymalizator nie zna liczności funkcji tabeli i będzie musiał zgadnąć. Może to być potencjalnie szkodliwe dla planów wykonania, więc to rozwiązanie można rozszerzyć, aby zapewnić statystyki wykonania dla optymalizatora.
Możesz zobaczyć to oszacowanie optymalizatora, uruchamiając EXPLAIN PLAN dla powyższego zapytania:
Mimo że kolekcja ma tylko 3 wartości, optymalizator oszacował dla niej 8168 wierszy (wartość domyślna). Na początku może się to wydawać nieistotne, ale optymalizatorowi może wystarczyć podjęcie decyzji o planie nieoptymalnym.
Rozwiązaniem jest użycie rozszerzeń optymalizatora w celu zapewnienia statystyk dla kolekcji:
Testowanie powstałego planu wykonania:
Jak widać, liczność na powyższym planie nie jest już domyślną wartością 8196. Wciąż nie jest to poprawne, ponieważ przekazujemy do funkcji kolumnę zamiast literału ciągu.
Pewne poprawki w kodzie funkcji byłyby konieczne, aby dać dokładniejsze oszacowanie w tym konkretnym przypadku, ale myślę, że ogólna koncepcja jest tutaj dość dobrze wyjaśniona.
Funkcja str2tbl użyta w tej odpowiedzi została pierwotnie opracowana przez Toma Kyte'a: https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:110612348061
Koncepcję kojarzenia statystyk z typami obiektów można dokładniej zbadać, czytając ten artykuł: http://www.oracle-developer.net/display.php?id=427
Opisana tutaj technika działa w 10g +.
źródło
REGEXP_COUNT nie został dodany do Oracle 11i. Oto rozwiązanie Oracle 10g, przejęte z rozwiązania Art.
źródło
Począwszy od Oracle 12c możesz użyć
JSON_TABLE
iJSON_ARRAY
:I zapytanie:
Wynik:
db <> fiddle demo
źródło
Oto alternatywna implementacja wykorzystująca XMLTABLE, która umożliwia rzutowanie na różne typy danych:
... lub jeśli ciągi rozdzielane są przechowywane w jednym lub kilku wierszach tabeli:
źródło
Chciałbym dodać inną metodę. Ten używa zapytań rekurencyjnych, czego nie widziałem w innych odpowiedziach. Jest obsługiwany przez Oracle od 11gR2.
Jest dość elastyczny dzięki charakterowi rozszczepiania. Po prostu zmień to w
INSTR
rozmowach.źródło
Bez używania connect by lub regexp :
źródło
Miałem ten sam problem i xmltable mi pomogło:
SELECT id, trim (COLUMN_VALUE) tekst FROM t, xmltable (('"' || REPLACE (tekst, ',', '", "') || '"'))
źródło
W Oracle 11g i nowszych można użyć rekursywnego zapytania podrzędnego i prostych funkcji łańcuchowych (które mogą być szybsze niż wyrażenia regularne i skorelowane hierarchiczne zapytania podrzędne):
Konfiguracja Oracle :
Zapytanie :
Wyjście :
db <> skrzypce tutaj
źródło
Użyłem funkcji DBMS_UTILITY.comma_to _table, która faktycznie działa z kodem w następujący sposób
użyłem własnych nazw tabel i kolumn
źródło
comma_to_table()
działa tylko z tokenami, które pasują do konwencji nazewnictwa obiektów bazy danych Oracle. Rzuci się na strunę, jak'123,456,789'
na przykład.