convert string into rows with SQL - sql

I have a string, like:
'one,two,three'
and I want to convert it in rows and use it into IN clause in an SQL:
one
two
three
I tried something like :
SELECT column_value
FROM XMLTable('"one","two","three"');
and it worked fine but in a join condition it fails.
SELECT 1
FROM dual
WHERE 'one' IN (SELECT column_value
FROM XMLTable('"one","two","three"'));
it gaves me the error:
ORA-00932: inconsistent datatypes: expected - got CHAR
00932. 00000 - "inconsistent datatypes: expected %s got %s"
Can anyone help me on this, please?
NOTE: I would like not use PLSQL

What you need is nothing but just casting a CLOB value to a [VAR]CHAR[2] data type such as
SELECT 1
FROM dual
WHERE 'one' IN (SELECT CAST(column_value AS VARCHAR2(20))
FROM XMLTable('"one","two","three"'))
1
---
1
in order to make it comparable with a literal(such as 'one').
Moreover, CAST might be replaceable with XMLCast as well.

You do not need XML to split the string and can use simple (fast) string functions:
WITH data (value) AS (
SELECT 'one,two,three' FROM DUAL
),
bounds (value, spos, epos) AS (
SELECT value, 1, INSTR(value, ',', 1)
FROM data
UNION ALL
SELECT value, epos + 1, INSTR(value, ',', epos + 1)
FROM bounds
WHERE epos > 0
)
SEARCH DEPTH FIRST BY value SET order_id
SELECT CASE epos
WHEN 0
THEN SUBSTR(value, spos)
ELSE SUBSTR(value, spos, epos-spos)
END as item
FROM bounds;
Which outputs:
ITEM
one
two
three
However
In your case you have an XY-problem and you DO NOT NEED to split the string as you can use LIKE to match the search string against a sub-string of your list:
SELECT 1
FROM dual
WHERE ',' || :your_list || ',' LIKE '%,' || :search_value || ',%';
or with hardcoded strings:
SELECT 1
FROM dual
WHERE ',' || 'one,two,three' || ',' LIKE '%,' || 'one' || ',%';
db<>fiddle here

Query that raised an error - if rewritten to this - works:
SQL> select 1
2 from dual
3 where 'one' in (select regexp_substr('one,two,three', '[^,]+', 1, level)
4 from dual
5 connect by level <= regexp_count('one,two,three', ',') + 1
6 );
1
----------
1
SQL>
Subquery (that uses regexp_substr) splits a comma-separated list of values ('one,two,three') into rows.
Alternatively, if you use Oracle Apex (or have it installed in your database), you can simplify it by utilizing apex_string.split:
SQL> select 1
2 from dual
3 where 'one' in (select * from apex_string.split('one,two,three', ','));
1
----------
1
SQL>

You could use REGEXP_COUNT to find your word in the list.
This would be faster than converting
But read this canonical thread Is storing a delimited list in a database column really that bad?
SELECT
CASE WHEN REGEXP_COUNT('one,two,three', '^([^,]+,)*one(,[^,]+)*$', 1, 'i') > 0
THEN 1 ELSE 0
END as cnt
FROM DUAL;
| CNT |
| --: |
| 1 |
db<>fiddle here

Related

How to Select a substring in Oracle SQL from and up to some specific characters?

i am using oracle sql. i would like to substr starting from characters XY0 and include 2 or 3 more characters until '-' sign in the string
These characters may be anywhere in the string.
Original
column_value
1st Row - Error due to XY0066- Does not fit -Not suitable
2nd Row -Error due to specific XY0089- Will not match
3rd Row -Not in good cond XY0215- Special type error
Extraction should be
result
XY0066
XY0089
XY0215
How can I do this?
You can use:
SELECT id,
SUBSTR(value, start_pos, end_pos - start_pos) AS code
FROM (
SELECT id,
value,
INSTR(value, 'XY') AS start_pos,
INSTR(value, '-', INSTR(value, 'XY') + 2) AS end_pos
FROM table_name
);
or
SELECT id,
SUBSTR(
value,
INSTR(value, 'XY'),
INSTR(value, '-', INSTR(value, 'XY') + 2) - INSTR(value, 'XY')
) AS code
FROM table_name;
or using regular expressions, which is shorter to type but will run much slower:
SELECT id,
REGEXP_SUBSTR(value, 'XY[^-]*') AS code
FROM table_name;
Which, for the sample data:
CREATE TABLE table_name (id, value) AS
SELECT 1, 'Error due to XY0066- Does not fit -Not suitable' FROM DUAL UNION ALL
SELECT 2, 'Error due to specific XY0089- Will not match' FROM DUAL UNION ALL
SELECT 3, 'Not in good cond XY0215- Special type error' FROM DUAL;
All output:
ID
CODE
1
XY0066
2
XY0089
3
XY0215
fiddle

Number formatting in Oracle SQL

I have some problem here. I got to SELECT a column which the result i.e. '01201698765'. How to split this number to becoming like this : '01.2016.98765'.
I've used TO_CHAR, but the '0' (zero) number at the front was gone.
You could use:
SUBSTR
concatenation operator ||
For example,
SQL> WITH sample_data AS(
2 SELECT '01201698765' num FROM dual
3 )
4 --end of sample_data mimicking real table
5 SELECT num,
6 substr(num, 1, 2)||'.'||substr(num, 3, 4)||'.'||substr(num, 7) num_formatted
7 FROM sample_data;
NUM NUM_FORMATTED
----------- -------------
01201698765 01.2016.98765
SQL>
Assuming the column is a string, just use string operations:
select substr(col, 1, 2) || '.' + substr(col, 3, 4) + '.' + substr(col, 5, 5)

Oracle SQL inserting parenthesis into phone number

The original format:
123-456-7890
My goal format:
(123)456-7890
I wanted to go the route of concatenating between substrings but I continually get flagged for errors. I am unsure of a better method to go about implementing a way to format.
My query:
select || '(' || substr(telephone,0, 3)|| ')' ||
substr(telephone,4, 3)|| ' '||
substr(telephone,7, 4)) as telephone,
from book;
My current error:
"missing expression"
You have an extra dangling parenthesis at the end of your SELECT, and you also have a dangling concatenation operator || in the front. Try this:
SELECT '(' || SUBSTR(telephone, 0, 3) || ')' ||
SUBSTR(telephone, 4, 3) || ' ' || SUBSTR(telephone, 7, 4) AS telephone
FROM book
Update:
You should really use this query, because it turns out you also had a problem with forming your desired output as well:
SELECT '(' || SUBSTR(telephone, 1, 3) || ')' || SUBSTR(telephone, 5, 8) AS telephone
FROM book
You can use regular expressions to do.
select regexp_replace
(phoneNo,
'([[:digit:]]{3})\-([[:digit:]]{3})\-([[:digit:]]{4})',
'(\1)\2-\3'
)
from(
select '123-456-7890' as phoneNo from dual)
Output
(123)456-7890
SELECT '123-456-7890','('||SUBSTR('123-456-7890',1,3)||')'||SUBSTR('123-456-7890',5,8) FROM dual;
Using SUBSTR:
SQL> WITH sample_data AS(
2 SELECT '123-456-7890' num FROM dual
3 )
4 -- end of sample_data mimicking real table
5 SELECT num,
6 '('
7 || SUBSTR(num, 1, 3)
8 ||
9 ')'
10 || SUBSTR(num, 5, 8) AS my_num
11 FROM sample_data;
NUM MY_NUM
------------ ---------------
123-456-7890 (123)456-7890
SQL>
Remember, the index for SUBSTR starts from 1. It is bad practice to use 0 as starting index.
You could also do it using REGEXP_REPLACE.
Pattern: (\d{3})(-)(\d{3})(-)(\d{4})
Expression: regexp_replace(num, '(\d{3})(-)(\d{3})(-)(\d{4})', '(\1)\3\4\5')
For example,
SQL> WITH sample_data AS(
2 SELECT '123-456-7890' num FROM dual
3 )
4 -- end of sample_data mimicking real table
5 SELECT num,
6 regexp_replace(num, '(\d{3})(-)(\d{3})(-)(\d{4})', '(\1)\3\4\5') my_num
7 FROM sample_data;
NUM MY_NUM
------------ ---------------
123-456-7890 (123)456-7890
SQL>

using Oracle SQL - regexp_substr to split a record

I need to split the record for column CMD.NUM_MAI which may contain ',' or ';'.
I did this but it gave me an error:
SELECT REGEXP_SUBSTR (expression.num_mai,
'[^;|,]+',
1,
LEVEL)
FROM (SELECT CMD.num_cmd,
(SELECT COMM.com
FROM COMM
WHERE COMM.cod_soc = CMD.cod_soc AND COMM.cod_com = 'URL_DSD')
AS cod_url,
NVL (CONTACT.nom_cta, TIERS.nom_ct1) AS nom_cta,
NVL (CONTACT.num_mai, TIERS.num_mai) AS num_mai,
NVL (CONTACT.num_tel, TIERS.num_tel) AS num_tel,
TO_CHAR (SYSDATE, 'hh24:MI') AS heur_today
FROM CMD, TIERS, CONTACT
WHERE ( (CMD.cod_soc = :CMD_cod_soc)
AND (CMD.cod_eta = :CMD.cod_eta)
AND (CMD.typ_cmd = :CMD.typ_cmd)
AND (CMD.num_cmd = :CMD.num_cmd))
AND (TIERS.cod_soc(+) = CMD.cod_soc)
AND (TIERS.cod_trs(+) = CMD.cod_trs_tra)
AND (TIERS.cod_soc = CONTACT.cod_soc(+))
AND (TIERS.cod_trs = CONTACT.cod_trs(+))
AND (CONTACT.lib_cta(+) = 'EDITION')) experssion
CONNECT BY REGEXP_SUBSTR (expression.num_mai,'[^;|,]+',1,LEVEL)
Error 1:
The expression in CONNECT BY clause is unary. You have to specify both left and right hand side operands.
Try something like,
CONNECT BY REGEXP_SUBSTR (expression.num_mai,'[^;|,]+',1,LEVEL) IS NOT NULL
Error 2:
Your bind variable name is wrong. Ex: :CMD_cod_eta
Perhaps you wanted this way!
( (CMD.cod_soc = :CMD_cod_soc)
AND (CMD.cod_eta = :CMD_cod_eta)
AND (CMD.typ_cmd = :CMD_typ_cmd)
AND (CMD.num_cmd = :CMD_num_cmd))
This is a common question, I'd put into a function, then call it as needed:
CREATE OR REPLACE function fn_split(i_string in varchar2, i_delimiter in varchar2 default ',', b_dedup_tokens in number default 0)
return sys.dbms_debug_vc2coll
as
l_tab sys.dbms_debug_vc2coll;
begin
select regexp_substr(i_string,'[^' || i_delimiter || ']+', 1, level)
bulk collect into l_tab
from dual
connect by regexp_substr(i_string, '[^' || i_delimiter || ']+', 1, level) is not null
order by level;
if (b_dedup_tokens > 0) then
return l_tab multiset union distinct l_tab;
end if;
return l_tab;
end;
/
This will return a table of varchar2(1000), dbms_debug_vc2coll, which is a preloaded type owned by SYS (or you could create your own type using 4000 perhaps). Anyway, an example using it (with space, comma, or semi-colon used as delimiters):
with test_data as (
select 1 as id, 'A;test;test;string' as test_string from dual
union
select 2 as id, 'Another string' as test_string from dual
union
select 3 as id,'A,CSV,string' as test_string from dual
)
select d.*, column_value as token
from test_data d, table(fn_split(test_string, ' ,;', 0));
Output:
ID TEST_STRING TOKEN
1 A;test;test;string A
1 A;test;test;string test
1 A;test;test;string test
1 A;test;test;string string
2 Another string Another
2 Another string string
3 A,CSV,string A
3 A,CSV,string CSV
3 A,CSV,string string
You can pass 1 instead of 0 to fn_split to dedup the tokens (like the repeated "test" token above)

How to tokenize a string in Oracle and convert each token to NUMBER to use them in a query as part of IN clause?

Suppose I have a string '1,2,3'
I want to tokenize the string and convert each of the tokens into NUMBER. So the above string will be tokenized into :
1 NUMBER
2 NUMBER
3 NUMBER
The final intention is to use them in a query as part of IN clause as below :
select * from sample where type in (1,2,3) ;
How can I achieve this ? One important point here is the string can have different number of tokens in different situations. So it can be either '1,2,3' or '1,2' or '1,2,3,4' or even '1'.
Please help me out guys.
Thanks in advance.
Please try:
with test as
(
select '1,2,3' str from dual
)
select * from sample
where type in(
select regexp_substr (str, '[^,]+', 1, rownum) split
from test
connect by level <= length (regexp_replace (str, '[^,]+')) + 1);
Depending on what you are doing, it might be faster to convert the id to a String and look for it in your String. Just add a comma to the beginning and the end of your list.
SELECT id
FROM (SELECT 1 AS id FROM DUAL
UNION
SELECT 2 FROM DUAL
UNION
SELECT 3 FROM DUAL) idtable
WHERE ',' || '1,3,4,5' || ',' LIKE '%,' || idtable.id || ',%'