using regex_substr to get corresponding value from another column in oracle - oracle

I have the below query which works, which gives the nth string corresponding to the key I give in the where clause as names (separator being ##)
select names from (
select
regexp_substr('a##b##c##d##e##f','[^##]+', 1, level) as names,
rownum as nth
from dual
connect by regexp_substr('a##b##c##d##e##f', '[^##]+', 1, level) is not null
)
where nth in (
select nth from (
select
regexp_substr('150##200##13##8##51##61','[^##]+', 1, level) as names,
rownum as nth
from dual
connect by regexp_substr('150##200##13##8##51##61', '[^##]+', 1, level) is not null
)
where names = '200'
)
Now, I have a table temp with say 3 columns x,y and z where x has strings like a##b##c##d##e##f, y has 1##2##3##4##5##6 and z will have number like 1.
If I have a rows like
a##b##c##d##e##f 150##200##13##8##51##61 200
a##b##c##d##e##f 1##2##3##4##5##6 2
g##h##i##j##k##l 1##2##3##4##5##99 99
I want outputs like
a##b##c##d##e##f 150##200##13##8##51##61 200 b
a##b##c##d##e##f 1##2##3##4##5##6 2 b
g##h##i##j##k##l 1##2##3##4##5##99 99 l
simply plugging "temp" in place of dual in the above query takes long time as the db has over 50k rows. Any better solution or how do I fix this?

You may find a plain SQL solution, but I'd prefer to capsule the critical substring functionality in a function. After that the query is trivial
update
with tcols as (
select rownum colnum from dual connect by level <= 6 /* (max) number of columns */),
t2 as (
select x,y,z, colnum,
nth_substring(y,'#',colnum) subs
from regexp_tst, tcols
)
select
x,y,z, colnum,
nth_substring(x,'#',colnum) a
from t2
where subs = z
;
.
X Y Z A
---------------- ---------------- ---------- ----
a##b##c##d##e##f 1##2##3##4##5##6 1 a
a##b##c##d##e##f 1##2##3##4##5##6 2 b
g##h##i##j##k##l 1##2##3##4##5##6 3 i
The required function is as follows (You may want to adjust the trimming and the repeated delimieter logic)
create or replace function nth_substring( i_str VARCHAR2, i_del VARCHAR2, i_pos NUMBER)
/*
find n-th substring in delimited string
i_str input string e.g. a##b##c##d##e##f
i_del delimiter character
i_pos position of the strin starting from 1
*/
return VARCHAR2 is
BEGIN
return rtrim(ltrim(regexp_substr(i_str,'[^'||i_del||']+', 1, i_pos)));
END;
/

Related

oracle forms 12. how to display leading zero of decimal numbers

I wish to display decimal numbers (from a query) into text items.
if I set
:TXT_ITEM := '0,000123456789'
it works. But, if :TXT_ITEM is bound to a numeric table field, value is displayed as ,000123456789.
I'm trying to force format number on several triggers (post-change, when-new-record-instance, post-text...), unsuccessfully. On other hand, setting format_mask would force my DB value to a given number of decimal digits.
How can I get leading zero to be displayed?
See if any of these two options help.
Sample data:
SQL> create table test as
2 (select 12.34 col from dual union all
3 select 0.1234003 from dual union all
4 select -13.43432203 from dual union all
5 select 0.00012345221 from dual union all
6 select -0.002412428238234821 from dual
7 );
Table created.
SQL> desc test;
Name Null? Type
----------------------------------------- -------- ----------------------------
COL NUMBER
SQL> select col,
2 regexp_replace(col, '^(-?)([.,])', '\10\2') result1,
3 rtrim(to_char(col, 'fm90D999999999999999999999'), '.') result2
4 from test;
COL RESULT1 RESULT2
---------- ------------------------- -------------------------
12,34 12,34 12,34
,1234003 0,1234003 0,1234003
-13,434322 -13,43432203 -13,43432203
,000123452 0,00012345221 0,00012345221
-,00241243 -0,002412428238234821 -0,002412428238234821
SQL>
How would use it/them in Forms? Exactly like that - you'd e.g.
select regexp_replace(col, '^(-?)([.,])', '\10\2')
into :block.text_item
from your_table
where some_condition;
Seems the concerned numeric data in the table is of type
NUMBER(13,12)
in this case it's enough to set TXT_ITEMs Format Mask attribute within the Data part of Property Palette as
0D000000000000
with precision of 13 and scale of 12 values.
Considering the scale part is fixed, you can add more zeroes before D character depending on your column's precision value such as two zeroes before D are kept for NUMBER(14,12) or three zeroes for NUMBER(15,12).

Enumerate and add in a column of the select - Oracle

I need to enumerate my lines in a column of the select by a type. For example, I have three types which can be "RR", "RJ" and "RA", every time that a type "RR" appears I have to sum 3 and the other ones I have to sum only 1, creating a sequence like:
Type Number
RR 3
RR 6
RJ 7
RR 10
RJ 11
RA 12
RR 15
I have other fields in the select, so I used the ROW_NUMBER() function with all my order by fields, something like:
select
number,
[...]
type,
ROW_NUMBER() OVER (order by number, type [...] )*3 as sequence
from
my_table
order by number, type [...]
I also tried to use a case statement, but it doesn't aggregate the values.
Is it possible to do? I'm trying to use the ROW_NUMBER() function, but i can't get the result, only three by three.
You could use SUM:
select
number,
[...]
type,
SUM(CASE WHEN Type = 'RR' THEN 3 ELSE 1 END)
OVER (order by number, type [...] ) as sequence
from my_table
order by number, type [...]
Rextester Demo

Oracle Case clause can not evaluate an "AND" with tow or more expressions

Oracle behaves really extrange with the next query:
I am trying to evaluate these three records, one of them should show the column digitado = 1 because it accomplishes all the conditions, which are, NUM_DOCUMENTO_ENCABEZADO iS NOT NULL and ORIGEN_PLANILLA = 2
NUM_DOCUMENTO NUM_DOCUMENTO_ENCABEZADO ORIGEN_PLANILLA
8220568059 8220568059 2
8220681644 2
940723593097 1
select x.num_documento,
x.origen_planilla,
x.num_documento_encabezado,
case
when x.num_documento_encabezado > '0' and x.origen_planilla = 2 then
1
else
0
end digitado
from (
select h.num_documento,
h.num_documento_encabezado,
h.origen_planilla
from (
select a.num_documento,
c.num_documento num_documento_encabezado,
case when NVl(UPPER(a.txt_observacion),'X') like '%SGP%' THEN 1 ELSE 2 END origen_planilla
from epsis.ryc_recaudo a,
epsis.ryc_recaudo_unificado b,
epsis.ryc_documento_encabezado c
where a.fec_pago >= to_date('28082013','ddmmyyyy') ---aca se coloca el dia del ultimo proceso,
and a.fec_pago < to_date('25092013','ddmmyyyy')-- el cecaudo viene un dia atrasados
and b.num_documento(+) = a.num_documento
and c.num_documento(+) = b.num_documento --80595
and a.num_documento in ( '940723593097', '8220681644','8220568059')
) h,
epsis.ryc_divide_documento f,
epsis.ryc_documento_encabezado g
where f.num_documento(+) = h.num_documento
and g.num_documento(+) =f.num_division
group by h.num_documento,
h.num_documento_encabezado,
h.origen_planilla
) x
This is the result:
NUM_DOCUMENTO ORIGEN_PLANILLA NUM_DOCUMENTO_ENCABEZADO DIGITADO
8220568059 2 8220568059 0
8220681644 2 0
940723593097 1 0
The column DIGITADO should be "1" for the first record.
Oracle can not evaluate this "CASE" properly:
case
when x.num_documento_encabezado > '0' and x.origen_planilla = 2 then
1
else
0
end digitado
I have tried diferent things, for example if I change the previous code for this:
case
when length(x.num_documento_encabezado||x.origen_planilla) > 1 then
1
else
0
end digitado
This is the result:
NUM_DOCUMENTO ORIGEN_PLANILLA NUM_DOCUMENTO_ENCABEZADO DIGITADO
8220568059 2 8220568059 1
8220681644 2 0
940723593097 1 0
It works for every record, but that is not the point, the point is that oracle is not able to evaluate the "AND" expression, and the ortiginal query is much longer than the example displayed.
Now, another extrange this is that, when I execute the query only for the record that is ok, I mean this
and a.num_documento in ('8220568059')
the "AND" expression in the case sentence works properly with the original "CASE".
Result:
NUM_DOCUMENTO ORIGEN_PLANILLA NUM_DOCUMENTO_ENCABEZADO DIGITADO
8220568059 2 8220568059 1
Another thing is that, and here is where i believe the problem is, when no outer join in the second subquery, then the query runs ok, but I need that outer join, I am talking about this:
where f.num_documento(+) = h.num_documento
and g.num_documento(+) =f.num_division
I really don't want to rewrite the full query, does anyone know why this happen?
Create and insert statements, these ones reproduce the issue
create table tmp_origin
(
doc varchar2(30),
val number,
obs varchar2(30)
);
create table tmp_uni
(
doc varchar2(30),
doc_origin varchar2(30)
);
create table tmp_div
(
doc varchar2(30),
doc_div varchar2(30)
);
insert into tmp_origin values ('8220568059',100000, 'NORMAL');
insert into tmp_origin values ('8220681644',200000, 'NORMAL');
insert into tmp_origin values ('940723593097',300000, 'SGP');
commit;
insert into tmp_uni values ('8220568059','8220568059');
commit;
This is the query adapted to the above lines, I have also added some others cases, so you can compare and identify that the first one is not working
select x.num_documento,
x.origen_planilla,
x.num_documento_encabezado,
case
when x.num_documento_encabezado is not null and x.origen_planilla = 2 then
1
else
0
end digitado,
case
when length(x.num_documento_encabezado||x.origen_planilla) > 1 then
1
else
0
end digitado2,
case
when x.origen_planilla = 2 then
case
when x.num_documento_encabezado is not null then
1
else
0
end
else
0
end digitado3
from (
select h.num_documento,
h.num_documento_encabezado,
h.origen_planilla
from (
select a.doc num_documento,
b.doc num_documento_encabezado,
case when NVl(UPPER(a.obs),'X') like '%SGP%' THEN 1 ELSE 2 END origen_planilla
from tmp_origin a,
tmp_uni b
where a.doc in ( '940723593097', '8220681644','8220568059')
and b.doc(+) = a.doc
) h,
tmp_div f
where f.doc(+) = h.num_documento
group by h.num_documento,
h.num_documento_encabezado,
h.origen_planilla
) x
You should almost never use the comparison operators with VARCHAR2, it is almost never useful (except if you are writing a sorting algorithm). In your case especially, it doesn't do what you expect.
When you compare VARCHAR2s, the result will depend upon character ordering (for instance '2' is "greater" than '10' because 2 comes after 1 in the character table).
Consider:
SQL> select * from dual where '8220568059' > '0';
DUMMY
-----
X
SQL> select * from dual where ' 8220568059' > '0';
DUMMY
-----
Always use the right datatype for the right task. There is almost always only one datatype that will work correctly. You should always use NUMBER and explicit datatype conversion when working with numbers:
SQL> select * from dual where to_number('8220568059') > 0;
DUMMY
-----
X
Also if you just want to know if a value is NULL, please use the IS NOT NULL operator:
SQL> WITH DATA AS (
2 SELECT '8220568059' num_documento_encabezado,
3 2 origen_planilla FROM dual UNION ALL
4 SELECT '', 2 FROM dual UNION ALL
5 SELECT '', 1 FROM dual)
6 SELECT x.origen_planilla,
7 x.num_documento_encabezado,
8 CASE
9 WHEN x.num_documento_encabezado IS NOT NULL
10 AND x.origen_planilla = 2 THEN
11 1
12 ELSE
13 0
14 END digitado
15 FROM DATA x;
ORIGEN_PLANILLA NUM_DOCUMENTO_ENCABEZADO DIGITADO
--------------- ------------------------ ----------
2 8220568059 1
2 0
1 0

Oracle Function .How to compare previous value with Current value

My Source Data
ename Age
BAL N
BAL Y
BAL Y
YUV N
YUV Y
NAR N
Logic
if ( (ename <> Previous_Ename or Previous_AGE='N' ) then AGE = as is
Else AGE='Y'
Could you please let me know how to code this using Oracle funcaiton? i tried but in all case it not showing the desired result set.
i used
create or replace function () RETURN
VARCHAR2
IS
previous_name VARCHAR2 (9) := 'DUMMY';
previous_age VARCHAR2 (9) := 'Z';
BEGIN
For cur_rec in (select ename, age from tablename order by ename) LOOP
if ( cur_rec.ename <> previous_ename or previous_age ='N')
then return cur_rec.age; /** it is populating the result set with only "N"***/
else return 'Y';
end if;
previous_ename :=ename; /*** not sure whether this assignment is correct- im trying to assignt current value as previous value for next row reference.****/
previous_age :=age; /*** not sure whether this assignment is correct****/
END LOOP;
END
REsult im getting:- actually the result should be same as Source for this data scenerio
ename Age
BAL N
BAL N
BAL N
YUV N
YUV N
NAR N
I still don't understand what you're trying to achieve here (at all)... or why
The problem is that you're trying to to this in a function but you're returning the something almost immediately. By your order logic (in the comments) the first value will always be N because that's the first value in the ORDER BY. For every record in your table this will be true.
Use a MERGE statement instead:
merge into tmp n
using ( select rowid as rid
, ename
, age
, lag(age) over ( partition by ename order by age ) as lag_age
from tmp
) o
on ( n.rowid = o.rid )
when matched then
update
set n.age = case when lag_age is null then age
when lag_age = 'N' then age
else 'Y'
end
;
SQL Fiddle

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