ORACLE - Stringfunktionen

flowerpower

Mitglied
hallo, ich soll aus einem Satz, alle Worte entnehmen und in einer Tabelle mit Position ablegen, weitergehend, dann die Häufigkeit der Worte (wenn doppelt vorhanden).

Habe mit PL/SQL diverse String-Funktionen versucht (rtrim, ltrim), die Funktionen geben aber für mich keine verwertbaren Ausgaben. Mit PHP hätte ich die Funktion explode genommen, damit wäre es meiner Meinung nach relativ simpel. Unter PL/SQL komme ich nicht weiter.

Vielen Dank. flowerpower
 
Hi, ich habe leider auch keine Funktion wie EXPLODE gefunden und daher kurz was ausprogrammiert.

Meine Datentabelle sah so aus:
SQL:
create table words
(
	word		VARCHAR2(50),
	positions	VARCHAR2(100),
	wordcount	NUMBER(2)
);

Und hier die anonyme PL/SQL Prozedur die mir die Tabelle füllt:
SQL:
declare
	s_sentence	VARCHAR2(100)	:= 'das ist mein Satz und das Wort das kommt drei mal vor';
	s_word		VARCHAR2(100);
	TYPE space_list IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
	spaces		space_list;
	pos_beg		PLS_INTEGER := -1;
	pos_cnt		PLS_INTEGER := 1;
begin

	LOOP
		pos_beg := INSTR( s_sentence, ' ', 1, pos_cnt );
		
		IF pos_beg = 0 THEN
			spaces( pos_cnt ) := LENGTH( s_sentence ) + 1;
			EXIT;
		ELSE
			spaces( pos_cnt ) := pos_beg;
			pos_cnt := pos_cnt + 1;
		END IF;

	END LOOP;

	pos_beg := 1;
	FOR i IN spaces.FIRST..spaces.LAST LOOP
		
		s_word  := TRIM( SUBSTR( s_sentence, pos_beg, spaces(i)-pos_beg ) );
		pos_beg := spaces(i);
		MERGE 	INTO words w
		USING	( SELECT s_word AS word FROM DUAL ) f
		ON ( w.word = f.word )
		WHEN MATCHED THEN UPDATE SET
			positions = positions || ' ' || TO_CHAR( i ),
			wordcount = wordcount + 1
		WHEN NOT MATCHED THEN INSERT VALUES ( s_word, TO_CHAR( i ), 1 );
	END LOOP;

end;
/

Das Ergebnis:
SQL:
SELECT * FROM words;
WORD                                               POSITIONS             WORDCOUNT
-------------------------------------------------- -------------------- ----------
das                                                1 6 8                         3
ist                                                2                             1
mein                                               3                             1
Satz                                               4                             1
und                                                5                             1
Wort                                               7                             1
kommt                                              9                             1
drei                                               10                            1
mal                                                11                            1
vor                                                12                            1

So nun zu Erklärung, oder ist alles klar ? ;-)

In der ersten Schleife suche ich alle Leerzeichen im Satz und speicher mir die Positionen in einer Liste ab. In der zweiten Schleife laufe ich nun die Liste durch und hole mir per SUBSTR die einzelnen Wörter heraus.
Das MERGE Statement ersetzt ein INSERT und ein UPDATE, d.h. wenn ein Wort noch nicht in der Liste ist wird es mit seiner Position im Satz (i) und der Anzahl 1 aufgenommen, ansonsten wird die Anzahl um 1 erhöht und die Position im String angehängt. Hoffe das genügt so ?
 
Zuletzt bearbeitet von einem Moderator:
Da bin ich erstmal platt. Hast Du das auf die Schnelle aus dem Ärmel geschüttelt?
Also erstmal vielen, vielen Dank. Ich schaue mir das nach einer Portion Schlaf an.

Danke auch für die Erklärung, falls sich meine Bildungslücken blicken lassen.:suspekt:

Nochmal Danke. Wenn mir etwas unklar bleibt/wird darf ich sicher noch mal fragen.

Schönen Abend. Gruß. flowerpower
 
... bei mir kommt folgende Ausgabe.

1 das 1
2 ist 1
3 mein 1
4 Satz 1
5 und 1
6 das 1
7 Wort 1
8 das 1
9 kommt 1
10 drei 1
11 mal 1
12 vor 1

Leider werden die Ergebnisse nicht summiert und die Position nicht angezeigt.
 
Hmmm das kann ich so leider nicht nachvollziehen. Das würde ja bedeuten er findet im Merge Statement ON ( w.word = f.word ) das vorhandene Wort nicht und fügt ein neues ein.
Das Problem hatte ich am Anfang auch, aber nur weil er das Leerzeichen vor den Wörtern hat stehen lassen, was ich mit dem TRIM umgangen habe.

Andere Frage, ich habs unter 10.2.0.1 getestet, nutzt du eine ältere Version ?

Mir würden jetzt erstmal nur 2 Dinge einfallen:
1.) Primary Key auf Spalte "word" legen, das verhindert zumindest, dass das Wort öfter auftaucht und zeigt uns ob es wirklich ein Fehler beim MERGE ist.
2.) Das MERGE Statement in 2 Statements umschreiben, d.h. erst ein UPDATE machen, und wenn SQL%ROWCOUNT = 0 zurückkommt, dann ein INSERT mit neuen Werten.
 
ich nutze die 9.2.0.1.0 ( diese ist auch vorgegeben, bzw. wir arbeiten aussschliesslich auf dieser). Versuche nochmal etwas zu verändern und melde mich morgen noch einmal.

DANKE.

Gruß. flowerpower
 
Kurioserweise erscheint statt der Ausgabe des Wortes die Zahlen 1-12, als Position erscheint dann das Wort. Bin gerade mal auf "Fehlersuche". Ideen sind jederzeit willkommen. :)

Grüsse. flowerpower
 

Anhänge

  • 24331attachment.jpg
    24331attachment.jpg
    12,2 KB · Aufrufe: 132
Zuletzt bearbeitet:
Ich bekomme zumindest einen reinen Insert in die Tabelle nun hin, auch werden nun die Spalten korrekt belegt.
Könntest Du für einen Newbie noch mal die Merge-Funkton aus einander nehmen, habe heute schon den ganzen Tag hier dran gesessen. Mit deinem ersten Quelltext kommt immer obige Ausgabe :(

Danke. Grüsse. flowerpower
 
flowerpower hat gesagt.:
Könntest Du für einen Newbie noch mal die Merge-Funkton aus einander nehmen, habe heute schon den ganzen Tag hier dran gesessen.

Aber klar, also hier MERGE im einzelnen.

Also MERGE macht im Grunde folgendes:
1.) Ich gebe dem MERGE Statement eine Menge an Daten
SQL:
USING   ( SELECT s_word AS word FROM DUAL ) f
In unserem Fall ist es einfach nur das eine Wort aus dem Satz,könnte aber z.B. auch ein Select aus einer Tabelle sein...

2.) Ich sage dem Statement in welcher Tabelle ich meine Daten haben möchte
SQL:
MERGE   INTO words w

3.) Ich definiere woran MERGE erkennt, ob es den Satz schon gibt oder nicht, also im Normalfall meinen Primary Key
SQL:
ON ( w.word = f.word )
Bei uns ist es das gefundene Wort im Satz und die Spalte mit den Wörtern in der Tabelle

4.) Ich beschreibe was MERGE machen soll, falls es den Eintrag schon gibt
SQL:
        WHEN MATCHED THEN UPDATE SET
            positions = positions || ' ' || TO_CHAR( i ),
            wordcount = wordcount + 1
Wir hängen die Position des Wortes an den vorhandenen String an und zählen die Wortanzahl um eins hoch

5.) Ich beschreibe was MERGE machen soll wenn das Wort nicht gefunden wird
SQL:
WHEN NOT MATCHED THEN INSERT (word, positions, wordcount ) VALUES ( s_word, TO_CHAR( i ), 1 );
Klar, ein einfaches INSERT Statement

Somit macht das MERGE Statement also das Prüfen, "Ist es schon da?, dann UPDATE, sonst INSERT" überflüssig und vor allem schneller, da ich nicht jeden Satz prüfen muss, sondern auch eine ganze Datenmenge "mergen" kann.

Ich hab das Beispiel übrigens nochmal getestet und bei mir gehts einwandfrei. Ich kann mir jetzt nur noch vorstellen, dass entweder deine Tabellenspalten anders rum definiert sind als bei mir ? (DESC)
oder dass es tatsächlich Unterschiede wegen der Version gibt, wobei mir dann das Verhalten unter 9.2.0.1 nicht ganz klar wäre...?
 
Zuletzt bearbeitet von einem Moderator:
Danke für die Erklärungen. Merge hatte nicht funktioniert, aber ich hatte es anders gelöst.
Die Worte als PrimaryKey und dann ein Insert (wenn Wort schon existiert, dann gab es eine DUP_VAL_ON_INDEX-Exception und es wurde ein Update gemacht, wobei die Position ergänzt und das Vorkommen hoch gezählt wurde). Problematisch war, dass ich bei der Exception aus dem gesamten Block geflogen bin. Da habe ich eine Weile gebraucht.

Eine Lösung habe ich dahingehend gefunden das Insert in einer Procedure aufzurufen, somit beendet die Exception nur die Prozedur und verbleibt in der eigentlichen Loop.

Gruß. flowerpower
 

Neue Beiträge

Zurück