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>
Related
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
I want to add special character after 3rd character. Below is the example
Current Phone Number:- 123234567
Expected output :- (123)234-567
How can I do this ?
Concatenation, as you said, along with the substr function:
SQL> with test (col) as
2 (select 123234567 from dual)
3 select '(' || substr(col, 1, 3) || ')' ||substr(col, 4, 3) ||'-'|| substr(col, 7) result
4 from test;
RESULT
------------
(123)234-567
SQL>
Or, using regular expressions:
SQL> with test (col) as
2 (select 123234567 from dual)
3 select regexp_replace(col, '([0-9]{3})([0-9]{3})', '(\1)\2-') result_2
4 from test;
RESULT_2
------------
(123)234-567
SQL>
I have a select statement with multiple concats using ||. The statement worked fine until the data became too big so I used TO_CLOB for the select to get around "String concatenation too long" error. The report is now generated but only partially with the data being cut out after 1 rows and the 3rd column of 2nd row.
Here's a short version of my code:
SET pagesize 0
SET echo off
SET feedback off
SET verify off
PROMPT value1, value2, value3, difference
SELECT
TO_CLOB('field1' || ',' || num1 || ',' || num2 || ',' || diff || CHR(10) ||
'field2' || ',' || num1 || ',' || num2 || ',' || diff || CHR(10) ||
....... (there's about 80 such lines here) .......
)
from table
OUTPUT I'm getting as the report:
value1, value2, value3, difference
field1, num1, num2, diff
field2, num1, num2
and the rest is blank. The rest of about 80 rows are not being generated.
PS: I tested the query on sql developer and it works fine but does this when run in linux.
Please let me know if there's something I'm missing
You need to get Oracle to concatenate the individual values as CLOB data types; wrap each field in TO_CLOB (otherwise, it will try to concatenate then as strings and then after concatenating will try to convert the single string to a CLOB, which would have already failed if they totalled more than 4000 characters):
SELECT TO_CLOB( field1 ) || ',' || TO_CLOB( field2 ) || CHR(10)
|| TO_CLOB( field3 ) || ',' || TO_CLOB( field4 ) AS value
FROM table_name;
Which, for the sample data:
CREATE TABLE table_name ( field1, field2, field3, field4 ) AS
SELECT LPAD( 'A', 4000, 'A' ),
LPAD( 'B', 4000, 'B' ),
LPAD( 'C', 4000, 'C' ),
LPAD( 'D', 4000, 'D' )
FROM DUAL;
Outputs:
| VALUE |
| ------------------------------------------- |
| AAA ... x4000 ... AAA,BBB ... x4000 ... BBB |
| CCC ... x4000 ... CCC,DDD ... x4000 ... DDD |
It may be sufficient just to convert the first value in the list to a CLOB:
SELECT TO_CLOB( field1 ) || ',' || field2 || CHR(10)
|| field3 || ',' || field4 AS value
FROM table_name;
db<>fiddle here
If your intention is to generate a CSV file from the command line, I would highly recommend trying out SQLcl. It works just like SQLPlus, but has much more functionality similar to what you would find in SQL Developer but through a command line interface. It is Java based so you will need at least Java 8.
By setting the sqlformat option, you can quickly generate a CSV file with ease. Since it is 99% compatible with SQLPlus commands, you can still spool and set whatever other format options you need. You can see from the example that it will even properly escape " as "" if there is a " in the columns being selected to generate a properly structured CSV file.
Edit: Andrew Sayer pointed out in the comments that this functionality is also available in the normal SQLPlus client since client version 12.2 using the set markup csv on command.
Here is a quick demonstration:
SQL> SELECT LEVEL, LEVEL + 1 AS next_level, 'Level: "' || LEVEL || '"' AS level_text
2 FROM DUAL
3 CONNECT BY LEVEL <= 10;
LEVEL NEXT_LEVEL LEVEL_TEXT
________ _____________ ______________
1 2 Level: "1"
2 3 Level: "2"
3 4 Level: "3"
4 5 Level: "4"
5 6 Level: "5"
6 7 Level: "6"
7 8 Level: "7"
8 9 Level: "8"
9 10 Level: "9"
10 11 Level: "10"
10 rows selected.
SQL> set sqlformat csv
SQL> SELECT LEVEL, LEVEL + 1 AS next_level, 'Level: "' || LEVEL || '"' AS level_text
2 FROM DUAL
3 CONNECT BY LEVEL <= 10;
"LEVEL","NEXT_LEVEL","LEVEL_TEXT"
1,2,"Level: ""1"""
2,3,"Level: ""2"""
3,4,"Level: ""3"""
4,5,"Level: ""4"""
5,6,"Level: ""5"""
6,7,"Level: ""6"""
7,8,"Level: ""7"""
8,9,"Level: ""8"""
9,10,"Level: ""9"""
10,11,"Level: ""10"""
10 rows selected.
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)
I am trying to find a match with a given list of search parameters appearing anywhere in the given string. The search parameters can be OR or AND. REGEXP_LIKE with REPLACE works fine with OR (|) but not able to do for AND. Here is an example of OR:
select 'match' from dual WHERE REGEXP_LIKE('BCR081', REPLACE ('BCR;081', ';', '|')); --works
select 'match' from dual WHERE REGEXP_LIKE('BCR081', '(' || REPLACE ('BCR;081', ';', ').*?(') || ')');
-- Works when they are in order but order shouldn't matter.
select 'match' from dual WHERE REGEXP_LIKE('BCR081', '(' || REPLACE ('081;BCR', ';', ').*?(') || ')'); --I need this to work.
Is there something equivalent to
select 'match' from dual WHERE REGEXP_LIKE('BCR081', REPLACE ('BCR;081', ';', '&'));
Any help is greatly appreciated. I tried (look ahead?):
select 'match' from dual WHERE REGEXP_LIKE('BCR081','(?=' || REPLACE ('081;BCR', ';', ')(?=') || ')');
Note: The above is an example only, we can have anywhere from 1-20 search parameters. Also can't use the contains clause as it will throw too many results error.
We can tokenise the search string and look for one by one, by virtually generating rows.
with my_data as
(
select 'BCR081' as str , 'BCR;081' as pattern from dual
),
pattern_table as
(
select str, regexp_substr(pattern,'[^;]+',1,level) as pattern
from my_data
connect by level <= regexp_count(pattern,';') + 1
)
SELECT DECODE(
COUNT(DECODE(
INSTR(a.str,b.pattern),
0,
null,
1)
),
0,
'no match',
'match'
) as result
FROM my_data a, pattern_table b
WHERE a.str = b.str
GROUP BY a.str;