Formatting Oracle SQL column with non-standard format - sql

I have an Oracle database where a file location is stored. Unfortunately, it isn't properly formatted.
For example, the file location is C:\images\00\45\34\34.IMG and is stored in the database as: 00453434.
I am able to use CONCAT to put C:\images and .IMG around the column, but I can't format the actual location to put \s in.
I've tried to_char, and to_number but it requires a specified format.
(My crappy attempt: to_char(filename, '09"\"09"\"09"\"09'))
Is there any way in SQL to format freely?

One method... assuming fixed length of each segment meaning each path is 2 digits including file name.
select 'C:\images\'|| substr('00453434',1,2) || '\' ||
substr('00453434',3,2) || '\' ||
substr('00453434',5,2) || '\' ||
substr('00453434',7,2) || '.IMG' as fullPath from dual

If needed at multiple queries, creating a PL/SQL function can also solve your problem. This example also assumes that each path has 2 digits, but supports paths of different lengths:
CREATE OR REPLACE FUNCTION GET_FILENAME(ID IN VARCHAR2, PREFIX IN VARCHAR2, SUFFIX IN VARCHAR2) RETURN VARCHAR2 IS
i PLS_INTEGER;
r VARCHAR2(4000);
BEGIN
r := PREFIX;
FOR i IN 1..LENGTH(ID)/2 LOOP
r := r || '\' || SUBSTR(ID, 2*i-1, 2);
END LOOP;
RETURN r || SUFFIX;
END;
/
The function can then be used within your standard SQL queries (or view definitions) as follows:
WITH TA_FILES AS (
SELECT '12345678' AS ID FROM DUAL
)
SELECT GET_FILENAME(ID, 'C:\images', '.IMG') FROM TA_FILES

Related

Split comma separated string with Oracle

I have a query that pulls the content of a text separated by comma exactly as this:
INSERVICE JOB #: N19020200001
SERVICE_CENTER:SBY,OH_CIRCUIT:MALTA8501,CREW:3675,URD_PRINT:STG-123/S1,FEEDER:PFB969,ISOLATED_1:SCC-1-B969,ISOLATED_2:UDTB969-5,RECONDUCTOR:Y,JACKETED_CABLE:N,CABLE_CART:N,LIVE_FRONT:N,BOOM:null,BACK_HOE:null,EASY_HAULING:null
Is there a way in SQL I can select/partition it to be in separate fields as below (it is always consistent as above):
I have a custom string parser function you can use ...
NOTE: For headers, you can replace them like REPLACE('HEADER','') and get values with commas...
You can check below code and use it in your db:
FUNCTION STRING_PARSER(VAL VARCHAR2, POSITION VARCHAR2, DELIMITER VARCHAR2) RETURN VARCHAR2 IS
v_pos3 number;
v_pos4 number;
BEGIN
/* Return 3rd occurrence of '_' */
v_pos3 := INSTR(VAL, DELIMITER, 1, POSITION) + 1;
/* Return 4rd occurrence of '_' */
v_pos4 := INSTR(VAL, DELIMITER, 1, POSITION + 1);
return SUBSTR(VAL, v_pos3, v_pos4 - v_pos3);
END;
Usage :
select report_tools_pkg.string_parser(',1,2,3',2,',') from dual
Note: Add a ',' || columnname to your sql if you want to you use ,it as it is ...

Extract number from string with Oracle function

I need to create an Oracle DB function that takes a string as parameter. The string contains letters and numbers. I need to extract all the numbers from this string. For example, if I have a string like RO1234, I need to be able to use a function, say extract_number('RO1234'), and the result would be 1234.
To be even more precise, this is the kind of SQL query which this function would be used in.
SELECT DISTINCT column_name, extract_number(column_name)
FROM table_name
WHERE extract_number(column_name) = 1234;
QUESTION: How do I add a function like that to my Oracle database, in order to be able to use it like in the example above, using any of Oracle SQL Developer or SQLTools client applications?
You'd use REGEXP_REPLACE in order to remove all non-digit characters from a string:
select regexp_replace(column_name, '[^0-9]', '')
from mytable;
or
select regexp_replace(column_name, '[^[:digit:]]', '')
from mytable;
Of course you can write a function extract_number. It seems a bit like overkill though, to write a funtion that consists of only one function call itself.
create function extract_number(in_number varchar2) return varchar2 is
begin
return regexp_replace(in_number, '[^[:digit:]]', '');
end;
You can use regular expressions for extracting the number from string. Lets check it. Suppose this is the string mixing text and numbers 'stack12345overflow569'. This one should work:
select regexp_replace('stack12345overflow569', '[[:alpha:]]|_') as numbers from dual;
which will return "12345569".
also you can use this one:
select regexp_replace('stack12345overflow569', '[^0-9]', '') as numbers,
regexp_replace('Stack12345OverFlow569', '[^a-z and ^A-Z]', '') as characters
from dual
which will return "12345569" for numbers and "StackOverFlow" for characters.
This works for me, I only need first numbers in string:
TO_NUMBER(regexp_substr(h.HIST_OBSE, '\.*[[:digit:]]+\.*[[:digit:]]*'))
the field had the following string: "(43 Paginas) REGLAS DE PARTICIPACION".
result field: 43
If you are looking for 1st Number with decimal as string has correct decimal places, you may try regexp_substr function like this:
regexp_substr('stack12.345overflow', '\.*[[:digit:]]+\.*[[:digit:]]*')
To extract charecters from a string
SELECT REGEXP_REPLACE(column_name,'[^[:alpha:]]') alpha FROM DUAL
In order to extract month and a year from a string 'A0807' I did the following in PL/SQL:
DECLARE
lv_promo_code VARCHAR2(10) := 'A0807X';
lv_promo_num VARCHAR2(5);
lv_promo_month NUMBER(4);
lv_promo_year NUMBER(4);
BEGIN
lv_promo_num := REGEXP_SUBSTR(lv_promo_code, '(\d)(\d)(\d)(\d)');
lv_promo_month := EXTRACT(month from to_date(lv_promo_num, 'MMYY'));
DBMS_OUTPUT.PUT_LINE(lv_promo_month);
lv_promo_year := EXTRACT(year from to_date(lv_promo_num, 'MMYY'));
DBMS_OUTPUT.PUT_LINE(lv_promo_year);
END;

Selecting individual values from csv format in oracle pl sql

I have the following value in a column in Oracle db ('abc', 'xyz')
I want to extract the values separately like abc, xyz by removing ' and (). Is there a way to do it using INSTR and SUBSTR functions?
Thanks
Use this query:
with sample as (select '(''abc'', ''xyz'')' text from dual)
select substr(text,instr(text,'''',1,1) + 1,instr(text,'''',1,2) - instr(text,'''',1,1) - 1),
substr(text,instr(text,'''',1,3) + 1,instr(text,'''',1,4) - instr(text,'''',1,3) - 1)
from sample;
It would help to know what you want to do with the data once parsed. How it could be handled in SQL vs PL/SQL to achieve your requirement could be very different.
That said, here's one way to strip surrounding parens and remove single quotes at the same time during the select using the powerful regexp_replace(source_string, pattern_string, replace_string) :
WITH qry AS (SELECT '(' || '''abc''' || ',' || '''xyz''' || ')' orig_string
FROM dual
)
SELECT regexp_replace(orig_string, '[()'']', '' ) clean_string
FROM qry;
The regexp_replace pattern_string says to match a character class (defind by opening and closing square brackets) containing a left paren or a right paren or a single quote (quoted so Oracle sees it) and the replace_string replaces it with nothing.
Then, to parse the values remaining here's an example from by bag of tricks I got somewhere and tweaked for this case:
set serveroutput on
DECLARE
-- Build a string in the format "('abc','xyz')"
orig_string varchar2(20) := '(' || '''abc''' || ',' || '''xyz''' || ')';
CURSOR cur IS
WITH qry AS (SELECT regexp_replace(orig_string, '[()'']','' ) clean_string
FROM dual
)
SELECT regexp_substr(clean_string, '[^,]+', 1, ROWNUM) element
FROM qry
CONNECT BY LEVEL <= LENGTH(regexp_replace (clean_string, '[^,]+')) + 1;
BEGIN
FOR rec IN cur LOOP
dbms_output.put_line('Element:' || rec.element);
END LOOP;
END;
It basically loops through the elements and prints them. I'm sure you can adapt this to your situation.

UTL_MATCH-like function to work with CLOB

My question is: Is there a UTL_MATCH-like function which works with a CLOB rather than a VARCHAR2?
My specific problem is: I'm on an Oracle database. I have a bunch of pre-written queries which interface with Domo CenterView. The queries have variables in them defined by ${variableName}. I need to rewrite these queries. I didn't write the original so instead of figuring out what a good value for the variables should be I want to run the queries with the application and get what the query was from V$SQL.
So my solution is: Do a UTL_MATCH on the queries with the variable stuff in it and V$SQL.SQL_FULLTEXT. However, UTL_MATCH is limited to VARCHAR2 and the datatype of V$SQL.SQL_FULLTEXT is CLOB. So, this is why I'm looking for a UTL_MATCH-like function which works with a CLOB datatype.
Any other tips of how to accomplish this are welcome. Thanks!
Edit, about the tips. If you have a better idea of how to do this, let me just tell you some information I've got at my disposal. I have about 100 queries, they're all in an excel spreadsheet (the ones with the ${variableName} in them). So I could pretty easily use excel to write a query for me. I'm hoping to just union all those queries together and copy the output to another sheet. Anyway, maybe that's helpful if you're thinking there's a better way to do this.
An example: Let's say I have the following query from Domo:
select department.dept_name
from department
where department.id = '${selectedDepartmentId}'
;
I want to call something like this:
select v.sql_fulltext
from v$sql v
where utl_match.jaro_winkler_similarity(v.sql_fulltext,
'select department.dept_name
from department
where department.id = ''${selectedDepartmentId}''') > 90
;
And get something like this in return:
SQL_FULLTEXT
------------------------------------------
select department.dept_name
from department
where department.id = '154'
What I've tried:
I tried substringing the clob and casting it to a varchar. I was really hopeful this would work, but it gives me an error. Here's the code:
select v.sql_fulltext
from v$sql v
where utl_match.jaro_winkler_similarity( cast( substr (v.sql_fulltext, 0, 4000) as varchar2 (4000)),
'select department.dept_name
from department
where department.id = ''${selectedDepartmentId}''') > 90
;
And here's the error:
ORA-22835: Buffer too small for CLOB to CHAR or BLOB to RAW conversion (actual: 8000, maximum: 4000)
However, if I run this it works fine:
select cast(substr(v.sql_fulltext, 0, 4000) as varchar2 (4000))
from v$sql v
;
So I'm not sure what the problem is with casting the substring...
UTL_MATCH is a packaging for comparing strings with regards for checking how similar two strings are. Its functions evaluate strings and return scores. So all you're going to get is a number indicating (say) how many edits you need to turn ${variableName} into "Farmville" or "StackOveflow".
What you won't get is the actual differences: these two strings of text are identical except at offset 123 where it replaces ${variableName} with "Farmville".
Putting it like that suggests an alternative approach. Using INSTR() and SUBSTR() to locate instances of ${variableName} in your Domo CenterView queries and use those offsets to identify the different text in the v$sql.fulltext equivalents. You can do this with CLOB in PL/SQL with the DBMS_LOB package.
If the text you want to search has length <= 32767, then you can just convert the CLOB to VARCHAR2 using DBMS_LOB.SUBSTR:
select v.sql_fulltext
from v$sql v
where utl_match.jaro_winkler_similarity(dbms_lob.substr(v.sql_fulltext), 'select department.dept_name from department where department.id = ''${selectedDepartmentId}''') > 90 ;
I ended up creating a custom function for it. Here's the code:
CREATE OR REPLACE function match_clob(clob_1 clob, clob_2 clob) return number as
similar number := 0;
sec_similar number := 0;
sections number := 0;
max_length number := 3949;
length_1 number;
length_2 number;
vchar_1 varchar2 (3950);
vchar_2 varchar2 (3950);
begin
length_1 := length(clob_1);
length_2 := length(clob_2);
--dbms_output.put_line('length_1: '||length_1);
--dbms_output.put_line('length_2: '||length_2);
IF length_1 > max_length or length_2 > max_length THEN
FOR x IN 1 .. ceil(length_1 / max_length) LOOP
--dbms_output.put_line('((x-1)*max_length) + 1'||(x-1)||' * '||max_length||' = '||(((x-1)*max_length) + 1));
vchar_1 := substr(clob_1, ((x-1)*max_length) + 1, max_length);
vchar_2 := substr(clob_2, ((x-1)*max_length) + 1, max_length);
-- dbms_output.put_line('Section '||sections||' vchar_1: '||vchar_1||' ==> vchar_2: '||vchar_2);
sec_similar := UTL_MATCH.JARO_WINKLER_SIMILARITY(vchar_1, vchar_2);
--dbms_output.put_line('sec_similar: '||sec_similar);
similar := similar + sec_similar;
sections := sections + 1;
END LOOP;
--dbms_output.put_line('Similar: '||similar||' ==> Sections: '||sections);
similar := similar / sections;
ELSE
similar := UTL_MATCH.JARO_WINKLER_SIMILARITY(clob_1,clob_2);
END IF;
--dbms_output.put_line('Overall Similar: '||similar);
return(similar);
end;
/

What is the string concatenation operator in Oracle?

What is the string concatenation operator in Oracle SQL?
Are there any "interesting" features I should be careful of?
(This seems obvious, but I couldn't find a previous question asking it).
It is ||, for example:
select 'Mr ' || ename from emp;
The only "interesting" feature I can think of is that 'x' || null returns 'x', not null as you might perhaps expect.
There's also concat, but it doesn't get used much
select concat('a','b') from dual;
I would suggest concat when dealing with 2 strings, and || when those strings are more than 2:
select concat(a,b)
from dual
or
select 'a'||'b'||'c'||'d'
from dual
DECLARE
a VARCHAR2(30);
b VARCHAR2(30);
c VARCHAR2(30);
BEGIN
a := ' Abc ';
b := ' def ';
c := a || b;
DBMS_OUTPUT.PUT_LINE(c);
END;
output:: Abc def
There are two ways to concatenate Strings in Oracle SQL. Either using CONCAT function or || operator.
CONCAT function allows you to concatenate two strings together
SELECT CONCAT( string1, string2 ) FROM dual;
Since CONCAT function will only allow you to concatenate two values together. If you want to concatenate more values than two, you can nest multiple CONCAT function calls.
SELECT CONCAT(CONCAT('A', 'B'),'C') FROM dual;
An alternative to using the CONCAT function would be to use the || operator
SELECT 'My Name' || 'My Age' FROM dual;
Using CONCAT(CONCAT(,),) worked for me when concatenating more than two strings.
My problem required working with date strings (only) and creating YYYYMMDD from YYYY-MM-DD as follows (i.e. without converting to date format):
CONCAT(CONCAT(SUBSTR(DATECOL,1,4),SUBSTR(DATECOL,6,2)),SUBSTR(DATECOL,9,2)) AS YYYYMMDD