How to add new line separator into data for oracle - oracle

I'm working on displaying data from oracle.
is there a way to make the following data inside the table:
example :
'1.somedata, 2.somedata, 3.somedata, 4.somedata, 5.somedata'
to display like:
example:
'1. somedata
2. somedata
3. somedata
4. somedata
5. somedata'
on the interface?
do i add new line separator directly into the data?
or do i separator them into new line when i query it?
or is there any other simple way?
Thanks.

There are so many ways to do this, here is one if you are selecting from a column:
SELECT REPLACE ('1.somedata, 2.somedata, 3.somedata, 4.somedata, 5.somedata', ',', CHR (13) || CHR (10)) AS split
FROM DUAL;
1.somedata
2.somedata
3.somedata
4.somedata
5.somedata

I personally would use the listagg function and use '' as the delimiter.
SELECT LISTAGG(last_name, ' ')
WITHIN GROUP (ORDER BY hire_date, last_name) "Emp_list",
MIN(hire_date) "Earliest"
FROM employees
WHERE department_id = 30;

Remember that Apex is generating a web page, which means the end result is HTML. Apex, however, will also sometimes escape special HTML characters for you, like < and &. Since you're viewing a table, I assume the source of your data is a query and your "somedata" field is a single column. Try this:
SELECT REPLACE( somedata_column, ',', '<br />' )
FROM mytable
You don't say what version of Apex. In Apex 4.x, the column would need to be set to a Standard Report Column, which would stop Apex from the <br> elements. I forget what the column type is in Apex 5.x.

Check below sample query which converts coma separated list data into rows
SELECT substr( '1.AL,2.AL,3.AL,4.AL,5.AL,6.AL,',
( case when rownum = 1 then 1
else instr( '1.AL,2.AL,3.AL,4.AL,5.AL,6.AL,', ',', 1, rownum - 1 ) + 1
end ),
instr( substr( '1.AL,2.AL,3.AL,4.AL,5.AL,6.AL,',
( case when rownum = 1 then 1
else instr( '1.AL,2.AL,3.AL,4.AL,5.AL,6.AL,', ',', 1, rownum - 1 ) + 1
end )
), ',' ) - 1
) as data
FROM dual
CONNECT BY LEVEL <= length( '1.AL,2.AL,3.AL,4.AL,5.AL,6.AL,' ) - length ( replace('1.AL,2.AL,3.AL,4.AL,5.AL,6.AL,', ',') )
Hope this will help you!

Related

Passing comma seperated parameter values through Oracle SOAP request

I running a BI Publisher report through SOAP request. the BI Publisher data model contain the following condition :
WHERE PERIOD_NAME IN ( :P_PERIOD_NAME )
when i pass single period like "Jan-21" it works fine but then when i pass "Jan-21, Feb-21" it fails.
how to pass comma separated values ?
You are passing in a single string (that happens to contain commas) and not a comma-separated list of multiple values so your query is effectively:
WHERE period_name IN ( 'Jan-21, Feb-21' )
Instead, surround both the delimited list and the list item in delimiters (so that you do not get a partial match) and then match on the substring:
WHERE ', ' || :P_PERIOD_NAME || ', ' LIKE '%, ' || period_name || ', %'
db<>fiddle here
Let try this query
SELECT *
FROM TABLE1
WHERE 1=1
AND PERIOD_NAME IN (
select regexp_substr('Jan-21,Feb-21','[^,]+', 1, level) as period from dual
connect by regexp_substr('Jan-21,Feb-21', '[^,]+', 1, level) is not null
);

Sum Listagg in a query

I am trying to sum the values from the listagg query i made.
Expected result should be like this
SELECT LEGAL_ENTITY_ID, SUM(0 + 0 + 0 + 1) as Grandtotals
FROM V_VBA_DDCR_MAIN WHERE LEGAL_ENTITY_ID=6012346 AND ROWNUM=1
GROUP BY LEGAL_ENTITY_ID
| LEGAL_ENTITY_ID | GRANDTOTALS
1| 6012346 | 1
My listagg goes like this
SELECT LISTAGG(NVL2(FLDNM,0,1) , '+ ') WITHIN GROUP (ORDER BY FLDNM) FROM LE_MERGE_DDC_MAPPING
the purpose of this query is to count the number of null fields in a row. I created a table containing the list of fields that needs to be checked for null values.
Result :
fld1 + fld2 + fld3 + fld4
=
0 + 0 + 0 + 1
So I wrote my query like this:
SELECT LEGAL_ENTITY_ID, SUM(SELECT LISTAGG(NVL2(FLDNM,0,1) , '+ ') WITHIN GROUP
(ORDER BY FLDNM) FROM LE_MERGE_DDC_MAPPING) as Grandtotals
FROM V_VBA_DDCR_MAIN WHERE LEGAL_ENTITY_ID=6000132
GROUP BY LEGAL_ENTITY_ID
However, I am getting an error ORA-00936: missing expression.
I am not sure if Oracle allows you to do a sum of the listagg function.
Any help is appreciated.
Your SELECT LISTAGG(NVL2(FLDNM,0,1) , '+ ')... query will always get string result '0+ 0+ 0+ ...', unless you have rows in LE_MERGE_DDC_MAPPING which are null. That value is nothing to do with the V_VBA_DDCR_MAIN table. You could generate a string that contains the expressions:
SELECT LISTAGG('NVL2(' || FLDNM || ' ,0,1)' , '+ ') ...
But that would just leave you with a string containing 'NVL2(fld1 ,0,1)+ NVL2(fld2 ,0,1)+ ...'. You can't sum a string, and sum() can't evaluate a string as an expression. You would need to generate a dynamic SQL statement based on that string.
There is a way to do that with XML, which isn't entirely intuitive. You can use the dbms_xmlgen package to create an XML document (as a CLOB) which contains a node with the NVL2 result from your actual target table:
select dbms_xmlgen.getxml('select nvl2(' || fldnm || ',0,1) from v_vba_ddcr_main where legal_entity_id=6012346')
from le_merge_ddc_mapping map;
I won't show the generated XML here. You can then query that to get the individual values out:
select xmlquery('/ROWSET/ROW/*/text()'
passing xmltype(
dbms_xmlgen.getxml('select nvl2(' || fldnm || ',0,1) from v_vba_ddcr_main where Legal_Entity_Id=6012346')
)
returning content)
from le_merge_ddc_mapping;
And you can them sum those:
select sum(to_number(xmlquery('/ROWSET/ROW/*/text()'
passing xmltype(
dbms_xmlgen.getxml('select nvl2(' || fldnm || ',0,1) from v_vba_ddcr_main where legal_entity_id=6012346')
)
returning content))) as grandtotals
from le_merge_ddc_mapping;
You've suggested there is a link between the tables, to determine which fields are included, so you'd need to add that. And you've said there are duplicates, which you'd need to handle - preferably by eliminating them from your view, but with a subquery if necessary. This can be a starting point for you to develop from though.
Why use LISTAGG at all?
SELECT LEGAL_ENTITY_ID,
(
SELECT COUNT(1)
FROM LE_MERGE_DDC_MAPPING
WHERE FLDNM IS NULL
) as Grandtotals
FROM V_VBA_DDCR_MAIN
WHERE LEGAL_ENTITY_ID=6000132;
(Since you are filtering on a single LEGAL_ENTITY_ID you don't need the final GROUP BY)
Are you sure there shouldn't be some sort of join condition between the two tables?

How to swap values between before and after `=` using Oracle?

I have declared a value in parameter #Data as ACCOUNT_NO|none|M=ACCOUNT_NO,ADD1|none|M=ADD1
I need to get a result as ACCOUNT_NO=ACCOUNT_NO|none|M,ADD1=ADD1|none|M.
Which means I need to swap between the values before and after =
I have the SQL Server Query for achieving this but I need Oracle query.
Declare #Data varchar(100)='ACCOUNT_NO|none|M=ACCOUNT_NO,ADD1|none|M=ADD1';
WITH
myCTE1 AS
(
SELECT CAST('<root><r>' + REPLACE(#Data,',','</r><r>') + '</r></root>' AS XML) AS parts1
)
,myCTE2 AS
(
SELECT CAST('<root><r>' + REPLACE(p1.x.value('.','varchar(max)'),'=','</r><r>') + '</r></root>' AS XML) as parts2
FROM myCTE1
CROSS APPLY parts1.nodes('/root/r') AS p1(x)
)
SELECT STUFF
(
(
SELECT ',' + parts2.value('/root[1]/r[2]','varchar(max)') + '=' + parts2.value('/root[1]/r[1]','varchar(max)')
FROM myCTE2
FOR XML PATH(''),TYPE
).value('.','varchar(max)'),1,1,'');
Expected Output if I execute the query ACCOUNT_NO=ACCOUNT_NO|none|M,ADD1=ADD1|none|M. Can anyone give an idea to do this one?
Sounds like a job for REGEXP_REPLACE:
WITH datatab as (select 'ACCOUNT_NO|none|M=ACCOUNT_NO,ADD1|none|M=ADD1' info from dual)
select info,
regexp_replace(info, '([^=]+)=([^=,]+),([^=]+)=([^=,]+)', '\2=\1,\4=\3') new_info
from datatab;
INFO NEW_INFO
--------------------------------------------- ---------------------------------------------
ACCOUNT_NO|none|M=ACCOUNT_NO,ADD1|none|M=ADD1 ACCOUNT_NO=ACCOUNT_NO|none|M,ADD1=ADD1|none|M
(as a complete aside, that's the first time I've ever written a regular expression and had it work first time. Apparently, I have gone over to the dark side... *{;-) )
ETA: If you need this in a procedure/function, you don't need to bother selecting the regular expression, you can do it in PL/SQL directly.
Here's an example of a function that returns the swapped over result:
create or replace function swap_places (p_data in varchar2)
return varchar2
is
begin
return regexp_replace(p_data, '([^=]+)=([^=,]+),([^=]+)=([^=,]+)', '\2=\1,\4=\3');
end swap_places;
/
-- example of calling the function to check the result
select swap_places('ACCOUNT_NO|none|M=ACCOUNT_NO,ADD1|none|M=ADD1') col1 from dual;
COL1
-------------------------------------------------
ACCOUNT_NO=ACCOUNT_NO|none|M,ADD1=ADD1|none|M

PL/SQL query IN comma deliminated string

I am developing an application in Oracle APEX. I have a string with user id's that is comma deliminated which looks like this,
45,4932,20,19
This string is stored as
:P5_USER_ID_LIST
I want a query that will find all users that are within this list my query looks like this
SELECT * FROM users u WHERE u.user_id IN (:P5_USER_ID_LIST);
I keep getting an Oracle error: Invalid number. If I however hard code the string into the query it works. Like this:
SELECT * FROM users u WHERE u.user_id IN (45,4932,20,19);
Anyone know why this might be an issue?
A bind variable binds a value, in this case the string '45,4932,20,19'. You could use dynamic SQL and concatenation as suggested by Randy, but you would need to be very careful that the user is not able to modify this value, otherwise you have a SQL Injection issue.
A safer route would be to put the IDs into an Apex collection in a PL/SQL process:
declare
array apex_application_global.vc_arr2;
begin
array := apex_util.string_to_table (:P5_USER_ID_LIST, ',');
apex_collection.create_or_truncate_collection ('P5_ID_COLL');
apex_collection.add_members ('P5_ID_COLL', array);
end;
Then change your query to:
SELECT * FROM users u WHERE u.user_id IN
(SELECT c001 FROM apex_collections
WHERE collection_name = 'P5_ID_COLL')
An easier solution is to use instr:
SELECT * FROM users u
WHERE instr(',' || :P5_USER_ID_LIST ||',' ,',' || u.user_id|| ',', 1) !=0;
tricks:
',' || :P5_USER_ID_LIST ||','
to make your string ,45,4932,20,19,
',' || u.user_id|| ','
to have i.e. ,32, and avoid to select the 32 being in ,4932,
I have faced this situation several times and here is what i've used:
SELECT *
FROM users u
WHERE ','||to_char(:P5_USER_ID_LIST)||',' like '%,'||to_char(u.user_id)||',%'
ive used the like operator but you must be a little carefull of one aspect here: your item P5_USER_ID_LIST must be ",45,4932,20,19," so that like will compare with an exact number "',45,'".
When using it like this, the select will not mistake lets say : 5 with 15, 155, 55.
Try it out and let me know how it goes;)
Cheers ,
Alex
Create a native query rather than using "createQuery/createNamedQuery"
The reason this is an issue is that you cannot just bind an in list the way you want, and just about everyone makes this mistake at least once as they are learning Oracle (and probably SQL!).
When you bind the string '32,64,128', it effectively becomes a query like:
select ...
from t
where t.c1 in ('32,64,128')
To Oracle this is totally different to:
select ...
from t
where t.c1 in (32,64,128)
The first example has a single string value in the in list and the second has a 3 numbers in the in list. The reason you get an invalid number error is because Oracle attempts to cast the string '32,64,128' into a number, which it cannot do due to the commas in the string.
A variation of this "how do I bind an in list" question has come up on here quite a few times recently.
Generically, and without resorting to any PLSQL, worrying about SQL Injection or not binding the query correctly, you can use this trick:
with bound_inlist
as
(
select
substr(txt,
instr (txt, ',', 1, level ) + 1,
instr (txt, ',', 1, level+1) - instr (txt, ',', 1, level) -1 )
as token
from (select ','||:txt||',' txt from dual)
connect by level <= length(:txt)-length(replace(:txt,',',''))+1
)
select *
from bound_inlist a, users u
where a.token = u.id;
If possible the best idea may be to not store your user ids in csv! Put them in a table or failing that an array etc. You cannot bind a csv field as a number.
Please dont use: WHERE ','||to_char(:P5_USER_ID_LIST)||',' like '%,'||to_char(u.user_id)||',%' because you'll force a full table scan although with the users table you may not have that many so the impact will be low but against other tables in an enterprise environment this is a problem.
EDIT: I have put together a script to demonstrate the differences between the regex method and the wildcard like method. Not only is regex faster but it's also a lot more robust.
-- Create table
create table CSV_TEST
(
NUM NUMBER not null,
STR VARCHAR2(20)
);
create sequence csv_test_seq;
begin
for j in 1..10 loop
for i in 1..500000 loop
insert into csv_test( num, str ) values ( csv_test_seq.nextval, to_char( csv_test_seq.nextval ));
end loop;
commit;
end loop;
end;
/
-- Create/Recreate primary, unique and foreign key constraints
alter table CSV_TEST
add constraint CSV_TEST_PK primary key (NUM)
using index ;
alter table CSV_TEST
add constraint CSV_TEST_FK unique (STR)
using index;
select sysdate from dual;
select *
from csv_test t
where t.num in ( Select Regexp_Substr('100001, 100002, 100003 , 100004, 100005','[^,]+', 1, Level) From Dual
Connect By Regexp_Substr('100001, 100002,100003, 100004, 100005', '[^,]+', 1, Level) Is Not Null);
select sysdate from dual;
select *
from csv_test t
where ('%,' || '100001,100002, 100003, 100004 ,100005' || ',%') like '%,' || num || ',%';
select sysdate from dual;
select *
from csv_test t
where t.num in ( Select Regexp_Substr('100001, 100002, 100003 , 100004, 100005','[^,]+', 1, Level) From Dual
Connect By Regexp_Substr('100001, 100002,100003, 100004, 100005', '[^,]+', 1, Level) Is Not Null);
select sysdate from dual;
select *
from csv_test t
where ('%,' || '100001,100002, 100003, 100004 ,100005' || ',%') like '%,' || num || ',%';
select sysdate from dual;
drop table csv_test;
drop sequence csv_test_seq;
Solution from Tony Andrews works for me. The process should be added to "Page processing" >> "After submit">> "Processes".
As you are Storing User Ids as String so You can Easily match String Using Like as Below
SELECT * FROM users u WHERE u.user_id LIKE '%'||(:P5_USER_ID_LIST)||'%'
For Example
:P5_USER_ID_LIST = 45,4932,20,19
Your Query Surely Will return Any of 1 User Id which Matches to Users table
This Will Surely Resolve Your Issue , Enjoy
you will need to run this as dynamic SQL.
create the entire string, then run it dynamically.

How do I display a field's hidden characters in the result of a query in Oracle?

I have two rows that have a varchar column that are different according to a Java .equals(). I can't easily change or debug the Java code that's running against this particular database but I do have access to do queries directly against the database using SQLDeveloper. The fields look the same to me (they are street addresses with two lines separated by some new line or carriage feed/new line combo).
Is there a way to see all of the hidden characters as the result of a query?I'd like to avoid having to use the ascii() function with substr() on each of the rows to figure out which hidden character is different.
I'd also accept some query that shows me which character is the first difference between the two fields.
Try
select dump(column_name) from table
More information is in the documentation.
As for finding the position where the character differs, this might give you an idea:
create table tq84_compare (
id number,
col varchar2(20)
);
insert into tq84_compare values (1, 'hello world');
insert into tq84_compare values (2, 'hello' || chr(9) || 'world');
with c as (
select
(select col from tq84_compare where id = 1) col1,
(select col from tq84_compare where id = 2) col2
from
dual
),
l as (
select
level l from dual
start with 1=1
connect by level < (select length(c.col1) from c)
)
select
max(l.l) + 1position
from c,l
where substr(c.col1,1,l.l) = substr(c.col2,1,l.l);
SELECT DUMP('€ÁÑ', 1016)
FROM DUAL
... will print something like:
Typ=96 Len=3 CharacterSet=WE8MSWIN1252: 80,c1,d1

Resources