How to recognize if a variable contains a newline character in Oracle - sql

I have a procedure which takes 2 variables as input, one among them may have string characters separated by enter or space.
Space however is easy to figure out, but how to we figure out if the variable has a newline character in it.
I have tried using chr(10) but no use, because I have to recognize if the strings are separated by a newline character. By the way even chr(10) doesn't work to insert string divided with space.
select 'ABC' || chr(10) || 'DEF' as c from dual
I currently have no such variable as input so can't even experiment to narrow down to some solutions, Also the above query isn't running fine, I mean it's not giving output like below.
ABC
DEF
I have also searched for different oracle documentation, but didn't find any.
Help would be appreciated.

A new line depends on the Operating system. In Unix based OS, it is CHR(10), in Windows it is CHR(13) followed by CHR(10).
You could use either of the following:
LIKE '%'||chr(10)||'%'
INSTR(column_name, chr(10)) > 0
Let's look at test cases in Windows OS:
SQL
Using LIKE
SQL> WITH DATA AS(
2 SELECT 'ABC' || chr(10) || 'DEF' AS c FROM dual UNION ALL
3 SELECT 'PQR' || ' ' || 'XYZ' AS c FROM dual UNION ALL
4 SELECT 'QWE' || CHR(13) || 'RTY' AS c FROM dual UNION ALL
5 SELECT 'no_space' AS c FROM dual
6 )
7 SELECT * FROM DATA WHERE c LIKE '%'||chr(10)||'%';
C
--------
ABC
DEF
SQL>
Using INSTR
SQL> WITH DATA AS(
2 SELECT 'ABC' || chr(10) || 'DEF' AS c FROM dual UNION ALL
3 SELECT 'PQR' || ' ' || 'XYZ' AS c FROM dual UNION ALL
4 SELECT 'QWE' || CHR(13) || 'RTY' AS c FROM dual UNION ALL
5 SELECT 'no_space' AS c FROM dual
6 )
7 SELECT * FROM DATA WHERE INSTR(c, chr(10)) > 0;
C
--------
ABC
DEF
SQL>
PL/SQL
Using LIKE
SQL> SET SERVEROUTPUT ON
SQL> DECLARE
2 c VARCHAR2(100);
3 BEGIN
4 c:='ABC' || chr(10) || 'DEF';
5 IF c LIKE '%'||chr(10)||'%' THEN
6 dbms_output.put_line('found chr(10)');
7 ELSE
8 dbms_output.put_line('not found');
9 END IF;
10 END;
11 /
found chr(10)
PL/SQL procedure successfully completed.
SQL>
Using INSTR
SQL> SET SERVEROUTPUT ON
SQL> DECLARE
2 c VARCHAR2(100);
3 BEGIN
4 c:='ABC' || chr(10) || 'DEF';
5 IF INSTR(c, chr(10)) > 0 THEN
6 dbms_output.put_line('found chr(10)');
7 ELSE
8 dbms_output.put_line('not found');
9 END IF;
10 END;
11 /
found chr(10)
PL/SQL procedure successfully completed.
SQL>

create table sample (a varchar2(100));
insert into sample (a) values ('HI_next_line
hello_next_line
hello2');
insert into sample (a) values ('singleline1');
insert into sample (a) values ('singleline2');
SQL> select * from sample;
A
-----------------------------------------------------------------------
HI_next_line
hello_next_line
hello2
singleline1
singleline2
SQL> select *
2 from sample
3 where instr(a, chr(10)) > 0;
A
-----------------------------------------------------------------------
HI_next_line
hello_next_line
hello2

Related

Multiline bind parameter

I'm trying the following query from DBeaver (backend is oracle):
SELECT * FROM mytable where mycolumn in (REPLACE( :req_id_list, CHR(13), ','))
when it prompts for the value of req_id_list I want to paste the values from Excel, which will be one value per line. the query is failing with ORA-00907: missing right parenthesis. is there a way to convert the multiline value to a CSV one ?
I don't use DBeaver so this is a SQL*Plus example. Basically, you'll have to "split" that multi-line input value into separate rows (that's what subquery in lines #8 - 10 does):
Declaring a bind variable:
SQL> var req_id_list varchar2(20)
Storing A + carriage return + C into it:
SQL> exec :req_id_list := 'A' || chr(13) || 'C';
PL/SQL procedure successfully completed.
Query itself (lines #1 - 5 represent sample data; I expect A and C to be returned as that's what I passed to req_id_list):
SQL> with mytable (mycolumn) as
2 (select 'A' from dual union all
3 select 'B' from dual union all
4 select 'C' from dual
5 )
6 select *
7 from mytable
8 where mycolumn in (select regexp_substr(replace(:req_id_list, chr(13), '#'), '[^#]+', 1, level)
9 from dual
10 connect by level <= regexp_count(:req_id_list, chr(13)) + 1
11 );
M
-
A
C
SQL>

TO_CLOB cutting off report

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.

How to use 'sysdate' if its a string constant

I extract data from a table, the field is mostly null, but sometimes it's sysdate. However since its from a table, after getting the field its 'sysdate', between single quotes. How can I use it?
I have tried to_date, to_date(to_char()).
I need something that works within
select to_date('sysdate') from dual;
You can use a case expression:
select case
when the_column = 'sysdate' then sysdate
else to_date(the_column)
end as date_value
from the_table;
The only way I know is dynamic SQL. Here's an example:
SQL> create table test (id number, col varchar2(20));
Table created.
SQL> insert into test
2 select 1, '''sysdate''' from dual union all
3 select 2, null from dual;
2 rows created.
SQL> declare
2 l_res test%rowtype;
3 l_str varchar2(200);
4 begin
5 for cur_r in (select id, col from test) loop
6 l_str := 'select ' || cur_r.id ||', '||
7 nvl(replace(cur_r.col, chr(39), null), 'null') || ' from dual';
8 execute immediate l_str into l_res;
9 dbms_output.put_line(l_res.id ||': '|| l_res.col);
10 end loop;
11 end;
12 /
1: 24.06.2019 12:18:39
2:
PL/SQL procedure successfully completed.
SQL>

my table contain data in below format and does not contain any automated primary key [duplicate]

I have column in my database where the values are coming like the following:
3862,3654,3828
In dummy column any no. of comma separated values can come. I tried with following query but it is creating duplicate results.
select regexp_substr(dummy,'[^,]+',1,Level) as dummycol
from (select * from dummy_table)
connect by level <= length(REGEXP_REPLACE(dummy,'[^,]+'))+1
I am not understanding the problem. Can anyone can help?
Works perfectly for me -
SQL> WITH dummy_table AS(
2 SELECT '3862,3654,3828' dummy FROM dual
3 )
4 SELECT trim(regexp_substr(dummy,'[^,]+',1,Level)) AS dummycol
5 FROM dummy_table
6 CONNECT BY level <= LENGTH(REGEXP_REPLACE(dummy,'[^,]+'))+1
7 /
DUMMYCOL
--------------
3862
3654
3828
SQL>
There are many other ways of achieving it. Read Split single comma delimited string into rows.
Update Regarding the duplicates when the column is used instead of a single string value. Saw the use of DBMS_RANDOM in the PRIOR clause to get rid of the cyclic loop here
Try the following,
SQL> WITH dummy_table AS
2 ( SELECT 1 rn, '3862,3654,3828' dummy FROM dual
3 UNION ALL
4 SELECT 2, '1234,5678' dummy FROM dual
5 )
6 SELECT trim(regexp_substr(dummy,'[^,]+',1,Level)) AS dummycol
7 FROM dummy_table
8 CONNECT BY LEVEL <= LENGTH(REGEXP_REPLACE(dummy,'[^,]+'))+1
9 AND prior rn = rn
10 AND PRIOR DBMS_RANDOM.VALUE IS NOT NULL
11 /
DUMMYCOL
--------------
3862
3654
3828
1234
5678
SQL>
Update 2
Another way,
SQL> WITH dummy_table AS
2 ( SELECT 1 rn, '3862,3654,3828' dummy FROM dual
3 UNION ALL
4 SELECT 2, '1234,5678,xyz' dummy FROM dual
5 )
6 SELECT trim(regexp_substr(t.dummy, '[^,]+', 1, levels.column_value)) AS dummycol
7 FROM dummy_table t,
8 TABLE(CAST(MULTISET
9 (SELECT LEVEL
10 FROM dual
11 CONNECT BY LEVEL <= LENGTH (regexp_replace(t.dummy, '[^,]+')) + 1
12 ) AS sys.OdciNumberList)) LEVELS
13 /
DUMMYCOL
--------------
3862
3654
3828
1234
5678
xyz
6 rows selected.
SQL>
Giving a PL/SQL example where parsing over a table with an ID and column name. This will parse and print out each ID and the parsed value which could then be inserted into a new table or used in some other way.
Input
Column_ID Column_Name
123 (3862,3654,3828)
Output
Column_ID Column_Name
123 3862
123 3654
123 3828
PL/SQL Code
declare
table_name1 varchar2(1000);
string_to_parse varchar2(2000); -- assign string to table name
string_length number := 0; -- string length for loop
string_value varchar2(2000); -- string value to store value in
column_id number;
begin
--some table in the format '123' as column_id, '(3862,3654,3828)' as column_name
--remove the parenthesis or other special characters if needed
update some_table t
set t.column_name = regexp_replace(t.column_name,'\(|\)','');
commit;
for i in (
select * from some_table
) loop
column_id := i.column_id; --assign the id of the colors
string_to_parse := i.column_name; -- assign string to be parsed
if string_to_parse is null then
--at this point insert into a new table, or do whatever else you need
dbms_output.put_line(column_id || ' ' || string_value);
else
--String to parse is the comma
string_to_parse := string_to_parse||',';
string_length := length(string_to_parse) - length(replace(string_to_parse,',',''));
-- Loop through string from parameter
for i in 1 .. string_length loop
-- [^,] matches any character except for the ,
select regexp_substr(string_to_parse,'[^,]+',1,i)
into string_value -- stores value into string_value
from dual; -- dual is a dummy table to work around
--at this point insert into a new table, or do whatever else you need
dbms_output.put_line(column_id || ' ' || string_value);
--clear out the string value
string_value := null;
end loop;
end if;
end loop;
end;

Splitting comma separated values in Oracle

I have column in my database where the values are coming like the following:
3862,3654,3828
In dummy column any no. of comma separated values can come. I tried with following query but it is creating duplicate results.
select regexp_substr(dummy,'[^,]+',1,Level) as dummycol
from (select * from dummy_table)
connect by level <= length(REGEXP_REPLACE(dummy,'[^,]+'))+1
I am not understanding the problem. Can anyone can help?
Works perfectly for me -
SQL> WITH dummy_table AS(
2 SELECT '3862,3654,3828' dummy FROM dual
3 )
4 SELECT trim(regexp_substr(dummy,'[^,]+',1,Level)) AS dummycol
5 FROM dummy_table
6 CONNECT BY level <= LENGTH(REGEXP_REPLACE(dummy,'[^,]+'))+1
7 /
DUMMYCOL
--------------
3862
3654
3828
SQL>
There are many other ways of achieving it. Read Split single comma delimited string into rows.
Update Regarding the duplicates when the column is used instead of a single string value. Saw the use of DBMS_RANDOM in the PRIOR clause to get rid of the cyclic loop here
Try the following,
SQL> WITH dummy_table AS
2 ( SELECT 1 rn, '3862,3654,3828' dummy FROM dual
3 UNION ALL
4 SELECT 2, '1234,5678' dummy FROM dual
5 )
6 SELECT trim(regexp_substr(dummy,'[^,]+',1,Level)) AS dummycol
7 FROM dummy_table
8 CONNECT BY LEVEL <= LENGTH(REGEXP_REPLACE(dummy,'[^,]+'))+1
9 AND prior rn = rn
10 AND PRIOR DBMS_RANDOM.VALUE IS NOT NULL
11 /
DUMMYCOL
--------------
3862
3654
3828
1234
5678
SQL>
Update 2
Another way,
SQL> WITH dummy_table AS
2 ( SELECT 1 rn, '3862,3654,3828' dummy FROM dual
3 UNION ALL
4 SELECT 2, '1234,5678,xyz' dummy FROM dual
5 )
6 SELECT trim(regexp_substr(t.dummy, '[^,]+', 1, levels.column_value)) AS dummycol
7 FROM dummy_table t,
8 TABLE(CAST(MULTISET
9 (SELECT LEVEL
10 FROM dual
11 CONNECT BY LEVEL <= LENGTH (regexp_replace(t.dummy, '[^,]+')) + 1
12 ) AS sys.OdciNumberList)) LEVELS
13 /
DUMMYCOL
--------------
3862
3654
3828
1234
5678
xyz
6 rows selected.
SQL>
Giving a PL/SQL example where parsing over a table with an ID and column name. This will parse and print out each ID and the parsed value which could then be inserted into a new table or used in some other way.
Input
Column_ID Column_Name
123 (3862,3654,3828)
Output
Column_ID Column_Name
123 3862
123 3654
123 3828
PL/SQL Code
declare
table_name1 varchar2(1000);
string_to_parse varchar2(2000); -- assign string to table name
string_length number := 0; -- string length for loop
string_value varchar2(2000); -- string value to store value in
column_id number;
begin
--some table in the format '123' as column_id, '(3862,3654,3828)' as column_name
--remove the parenthesis or other special characters if needed
update some_table t
set t.column_name = regexp_replace(t.column_name,'\(|\)','');
commit;
for i in (
select * from some_table
) loop
column_id := i.column_id; --assign the id of the colors
string_to_parse := i.column_name; -- assign string to be parsed
if string_to_parse is null then
--at this point insert into a new table, or do whatever else you need
dbms_output.put_line(column_id || ' ' || string_value);
else
--String to parse is the comma
string_to_parse := string_to_parse||',';
string_length := length(string_to_parse) - length(replace(string_to_parse,',',''));
-- Loop through string from parameter
for i in 1 .. string_length loop
-- [^,] matches any character except for the ,
select regexp_substr(string_to_parse,'[^,]+',1,i)
into string_value -- stores value into string_value
from dual; -- dual is a dummy table to work around
--at this point insert into a new table, or do whatever else you need
dbms_output.put_line(column_id || ' ' || string_value);
--clear out the string value
string_value := null;
end loop;
end if;
end loop;
end;