I would need to reverse the word positions in a sentence or String.
For example : \"Hello World! I Love StackOverflow\", to be displayed as \"StackOverflow
XML-based version to avoid defining your own function; requires 11g for listagg()
:
select listagg(word, ' ') within group (order by rn desc) as reversed
from (
select word, rownum as rn
from xmltable('for $i in ora:tokenize($STR, " ") return $i'
passing 'Hello World! I Love StackOverflow' as str
columns word varchar2(4000) path '.'
)
);
REVERSED
----------------------------------------
StackOverflow Love I World! Hello
The XMLTable()
does the tokenising, and assigns a row number:
select rownum as rn, word
from xmltable('for $i in ora:tokenize($STR, " ") return $i'
passing 'Hello World! I Love StackOverflow' as str
columns word varchar2(4000) path '.'
);
RN WORD
---------- --------------------
1 Hello
2 World!
3 I
4 Love
5 StackOverflow
The listagg()
then pieces it back together in reverse order.
Create a Function:
REGEXP_SUBSTR('Your text here','[^ ]+', 1, ?)
will extract a word from the text using Space as a delimiter. Tt returns the original String itself on Exception!
CREATE OR REPLACE FUNCTION reverse_words (v_STRING IN VARCHAR2)
RETURN VARCHAR2
IS
L_TEMP_TEXT VARCHAR2(4000);
L_FINAL_TEXT VARCHAR2(4000);
V_LOOPCOUNT NUMBER :=0;
T_WORD VARCHAR2(4000);
BEGIN
L_TEMP_TEXT := regexp_replace(V_STRING,'[[:space:]]+',' '); -- Replace multiple spaces as single
LOOP
v_LOOPCOUNT := v_LOOPCOUNT+1;
T_WORD := REGEXP_SUBSTR(L_TEMP_TEXT,'[^ ]+', 1, V_LOOPCOUNT);
L_final_TEXT := T_WORD||' '||L_final_TEXT;
EXIT WHEN T_WORD IS NULL;
END LOOP;
RETURN(TRIM(L_final_TEXT));
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(sqlerrm||chr(10)||dbms_utility.format_error_backtrace);
RETURN V_STRING;
END reverse_words;
/
Sample Result:
You can call reverse_words(yourcolumn) from your_table
SQL> select reverse_words('Hello World! I Love StackOverflow') "Reversed" from dual;
Reversed
--------------------------------------------------------------------------------
StackOverflow Love I World! Hello
Here you go:
WITH sel_string AS
(SELECT 'Hello World! I Love StackOverflow' AS fullstring FROM DUAL)
SELECT SUBSTR(fullstring, beg + 1, end_p - beg - 1) AS token
FROM (SELECT beg, LEAD(beg) OVER (ORDER BY beg) AS end_p, fullstring
FROM (SELECT beg, fullstring
FROM (SELECT LEVEL beg, fullstring
FROM sel_string
CONNECT BY LEVEL <= LENGTH(fullstring))
WHERE INSTR(' ', SUBSTR(fullstring, beg, 1)) > 0
UNION ALL
SELECT 0, fullstring FROM sel_string
UNION ALL
SELECT LENGTH(fullstring) + 1, fullstring FROM sel_string))
WHERE end_p IS NOT NULL AND
end_p > beg + 1
ORDER BY ROWNUM DESC;
All in one SQL query. I wish I could claim credit for this query but I can't - found it years ago on the net and have used it ever since.
Share and enjoy.
One more solution
WITH str_tab(str1, rn) AS
(SELECT regexp_substr(str, '[^\[:space:]]+', 1, LEVEL),
LEVEL
FROM (SELECT 'Hello World! I Love StackOverflow' str
FROM dual) tab
CONNECT BY LEVEL <= LENGTH(str) - LENGTH(REPLACE(str, ' ')) + 1)
SELECT listagg(str1, ' ') WITHIN GROUP (ORDER BY rn DESC) AS new_text
FROM str_tab;
DECLARE
in_string VARCHAR2(500);
pros_string VARCHAR2(500);
out_string VARCHAR2(800);
spce_cnt NUMBER;
BEGIN
in_string := 'Hello World! I Love StackOverflow';
pros_string := ' '||in_string||' ' ;
spce_cnt := REGEXP_COUNT(pros_string,' ',1);
FOR i IN reverse 1.. spce_cnt-1
LOOP
out_string := out_string||' '|| SubStr (pros_string,InStr(pros_string, ' ',1,i)+1 ,InStr(SubStr (pros_string,InStr(pros_string, ' ',1,i)+1 ),' ' ));
Dbms_Output.Put_Line(out_string);
END LOOP;
END;