Oracle DECODE and NVL - oracle

I'm trying to analyze an existing oracle query. I am not well versed in Oracle and I'm bit confused by the use of this NVL function in DECODE function
Select
DECODE (DECODE(NVL(tab.id,0),0,0,1)+ DECODE (NVL(tab2.id,0),0,01), 2,'two', 0,'none',
DECODE (DECODE (NVL(tab2.id,0),0,0,1),1,'one','NA')
) Result
The tables are contains below values.
Tab
count id
1 1111
0 null
1 2222
1 3333
1 4444
1 5555
tab2
count id
1 1111
1 3333
1 6666
1 2222
0 null
How does a DECODE function work here?

How does a DECODE function work here?
DECODE is a function-based equivalent of a CASE statement.
Your code is the equivalent of:
Select CASE DECODE(NVL(tab.id,0),0,0,1)
+ DECODE (NVL(tab2.id,0),0,0,1),
WHEN 2 THEN 'two'
WHEN 0 THEN 'none'
ELSE DECODE(DECODE (NVL(tab2.id,0),0,0,1),1,'one','NA')
END AS Result
You can then expand out the nested DECODEs and NVLs to:
Select CASE CASE
WHEN tab.id IS NULL OR tab.id = 0 THEN 0
ELSE 1
END
+
CASE
WHEN tab2.id IS NULL OR tab2.id = 0 THEN 0
ELSE 1
END,
WHEN 2 THEN 'two'
WHEN 0 THEN 'none'
ELSE CASE CASE
WHEN tab2.id IS NULL OR tab2.id = 0 THEN 0
ELSE 1
END
WHEN 1 THEN 'one'
ELSE 'NA'
END
END AS Result
In English:
Covert each of tab.id and tab2.id to zero, if their values are either zero or NULL, or to one and then add the two values together.
If their total is zero or two then return the total in words.
If their total is one then check the tab2.id value on its own and if its value is not zero or NULL then return (in words) one.
Otherwise, it must have been tab.id that was not zero or NULL and return NA.

Related

Number of trailing zeros in a number

I have a column MONTHLY_SPEND in the table with data type of NUMBER. I am trying to write a query which will return number of zeros in the column.
e.g..
1000 will return 3
14322 will return 0
1230 will return 1
1254000.65 will return 0
I tried using mod operator and 10 but without the expected result. Any help is appreciated. Please note that database is Oracle and we can't create procedure/function.
select nvl(length(regexp_substr(column, '0+$')), 0) from table;
Here is one way to find
create table spend
(Monthly_spend NUMBER);
Begin
insert into spend values (1000)
insert into spend values (14322)
insert into spend values (1230)
insert into spend values (1254000.65)
End;
This query will for this data :
select Monthly_spend,REGEXP_COUNT(Monthly_spend,0)
from spend
where Monthly_spend not like '%.%' ;
if have one more data like 102 and if it should be zero , then try below query:
select Monthly_spend,case when substr(Monthly_spend,-1,1)=0 THEN REGEXP_COUNT(Monthly_spend,0) ELSE 0 END from spend;
Here is final query for value like 2300120 or 230012000
select Monthly_spend,
case when substr(Monthly_spend,-1,1)=0 and REGEXP_COUNT(trim (0 from Monthly_spend),0)<=0 THEN REGEXP_COUNT(Monthly_spend,0)
when REGEXP_COUNT(trim (0 from Monthly_spend),0)>0 THEN LENGTH(Monthly_spend) - LENGTH(trim (0 from Monthly_spend))
ELSE 0 END from spend;
Output :
1000 3
1254000.65 0
14322 0
1230 1
102 0
2300120 1
230012000 3
You can try this, a simple solution.
select length(to_char(col1))-length(rtrim(to_char(col1), '0')) no_of_trailing_zeros from dual;
select length(to_char('123.120'))-length(rtrim(to_char('123.120'), '0')) no_of_trailing_zeros from dual;

Passing dynamic values to order records in oracle

I want to sort a record in following way.
Arrange records in group (by ID column)
Sort the step 1 results by ascending order (by NAME column)
2.1. If NAME Column having same values, then order by FLAG column value (ascending order)
Order the Step 2 results by Order Assist column (I will be passing dynamic value to sort using order assist column)
My Query:
SELECT IDENTIFIER, CODE, INC_EXC_FLAG,ORDER_ASSIST FROM DUMMY_SORT
WHERE METHOD_ID = '1'
GROUP BY (IDENTIFIER, CODE, INC_EXC_FLAG,ORDER_ASSIST)
ORDER BY ORDER_ASSIST ASC, CODE ASC, INC_EXC_FLAG ASC
Result of above Query:
ID NAME FLAG ORDER_ASSIST
A_EC AEC 0 EC1
B_EC_DET BEC 1 EC2
A_NIT ANIT 0 NIT1
A_NIT ANIT 1 NIT1
A_NIT BNIT 0 NIT1
B_NIT_DET BNIT 0 NIT2
B_NIT_DET BNIT 1 NIT2
A_SC ASC 0 SC1
A_SC ASC 1 SC1
B_SC_DET BSC 0 SC2
B_SC_DET BSC 1 SC2
C_SC_FUN CSC 0 SC3
D_SC_GRP DSC 0 SC4
But I want to generate the result according to dynamic values of order_assist
For Example:
If I am passing dynamic value as "SC" i want to fisrt order the records SC1,SC2,SC3. Then NIT1,NIT2 . then EC1,EC2.
If I am passing dynamic value as "NITG" i want to fisrt order the records NIT1,NIT2 then SC1,SC2,SC3. then EC1,EC2.
Expected result added when dynamic value is "SC"
ID NAME FLAG ORDER_ ASSIST
A_SC ASC 0 SC1
A_SC ASC 1 SC1
B_SC_DET BSC 0 SC2
B_SC_DET BSC 1 SC2
C_SC_FUN CSC 0 SC3
D_SC_GRP DSC 0 SC4
A_NIT ANIT 0 NIT1
A_NIT ANIT 1 NIT1
A_NIT BNIT 0 NIT1
B_NIT_DET BNIT 0 NIT2
B_NIT_DET BNIT 1 NIT2
A_EC AEC 0 EC1
B_EC_DET BEC 1 EC2
Sounds like maybe you're after something like:
order by case when p_sort_param = 'SC' and order_assist like 'SC%' then 1
when p_sort_param = 'SC' and order_assist like 'NIT%' then 2
when p_sort_param = 'NITG' and order_assist like 'NIT%' then 1
when p_sort_param = 'NITG' and order_assist like 'SC%' then 2
else 3
end,
order_assist
where p_sort_param is the parameter that gets passed in to provide the "dynamic" value. This assumes you're running the query via a stored procedure. If it's a manually run query (eg. in Toad), then add a colon in front of the parameter name to make :p_sort_param.
I cannot understand your specific ordering rules, but you should be able to achieve what you want using CASE expressions:
order by
case order_assist
when 'SC' then <first thing to order by for SC>
when 'NITG' then <first thing to order by for NITG>
...
end,
case order_assist
when 'SC' then <second thing to order by for SC>
when 'NITG' then <second thing to order by for NITG>
...
end,
... etc.

ORACLE Group By & Duplicates

I come from a MySQL background and am having problems with the following query:
SELECT DISTINCT agenda.idagenda AS "ID_SERVICE", agenda.name AS "ID_SERVICE_NAME", specialities.id AS "ID_DEPARTMENT", specialities.name AS "ID_DEPARTMENT_NAME",
supervisor.clients_waiting AS "CWaiting",
(CASE WHEN supervisor.clients_resent_waiting_area IS NULL THEN 0 ELSE supervisor.clients_resent_waiting_area END) AS "CWaiting_Resent_Area",
supervisor.clients_attending AS "CAttending",
supervisor.clients_attended AS "CAttended",
(SELECT MAX(ROUND((SYSDATE-core.supervisor_time_data.time_attending)*86400)) FROM dual) AS "MTA",
(SELECT MAX(ROUND((SYSDATE-core.supervisor_time_data.time_waiting)*86400)) FROM dual) AS "MTE",
(SELECT SUM(SYSDATE-supervisor_time_data.time_attending)*86400 FROM dual)/(SELECT supervisor.clients_attending FROM dual) AS "TMA",
(SELECT SUM(SYSDATE-supervisor_time_data.time_waiting)*86400 FROM dual)/(SELECT supervisor.clients_waiting FROM dual) AS "TME",
supervisor.tme_accumulated AS "TME_ACCUMULATED",
supervisor.tma_accumulated AS "TMA_ACCUMULATED",
(CASE WHEN agenda.alarm_cee IS NULL THEN 0 ELSE agenda.alarm_cee END) AS "ALARM_CEE",
(CASE WHEN agenda.alarm_mte IS NULL THEN 0 ELSE agenda.alarm_mte END) AS "ALARM_MTE",
(CASE WHEN agenda.alarm_mta IS NULL THEN 0 ELSE agenda.alarm_mta END) AS "ALARM_MTA",
(CASE WHEN agenda.alarm_tme IS NULL THEN 0 ELSE agenda.alarm_tme END) AS "ALARM_TME"
FROM CORE.supervisor
LEFT JOIN CORE.supervisor_time_data ON supervisor_time_data.id_service = supervisor.id_service
LEFT JOIN CORE.agenda ON supervisor.id_service = agenda.id
LEFT JOIN CORE.specialities ON agenda.idspeciality = specialities.id
WHERE supervisor.booked_or_sequential = 1
GROUP BY agenda.idagenda, agenda.name, supervisor.id_service, specialities.id, specialities.name, supervisor.clients_waiting, supervisor.clients_resent_waiting_area,
supervisor.clients_attending, supervisor.clients_attended,
supervisor_time_data.time_attending, supervisor_time_data.time_waiting,
supervisor.tme_accumulated, supervisor.tma_accumulated, agenda.alarm_cee, agenda.alarm_mte,agenda.alarm_mta,agenda.alarm_tme;
It should return two records, but instead it's returning four. ID_SERIVE is returning 3 records with the same value.
"ID_SERVICE" "ID_SERVICE_NAME" "ID_DEPARTMENT" "ID_DEPARTMENT_NAME" "CWaiting" "CWaiting_Resent_Area" "CAttending" "CAttended" "MTA" "MTE" "TMA" "TME" "TME_ACCUMULATED" "TMA_ACCUMULATED" "ALARM_CEE" "ALARM_MTE" "ALARM_MTA" "ALARM_TME"
"DR" "DR" 1 "SECUENCIALES" 1 0 1 1 5504 5504 21 109 0 0 0 0
"DR" "DR" 1 "SECUENCIALES" 1 0 1 1 1590 1590.000000000000000000000000000000000002 21 109 0 0 0 0
"DR" "DR" 1 "SECUENCIALES" 1 0 1 1 21 109 0 0 0 0
"TRAU" "TRAU" 1 "SECUENCIALES" 1 0 0 0 1567 1567.000000000000000000000000000000000002 0 0 0 0 0 0
What am I doing wrong?
Thanks
You seem to be including all the columns you're interesting in the group by, and not aggregating properly; possibly you've got to this point by trial and error as you've tried to resolve errors from columns not being grouped. You don't need the sub-selects in all the column clauses.
Untested as we don't have your tables or raw data but it looks like you want something like:
SELECT agenda.idagenda AS "ID_SERVICE",
agenda.name AS "ID_SERVICE_NAME",
specialities.id AS "ID_DEPARTMENT",
specialities.name AS "ID_DEPARTMENT_NAME",
supervisor.clients_waiting AS "CWaiting",
NVL(supervisor.clients_resent_waiting_area, 0) AS "CWaiting_Resent_Area",
supervisor.clients_attending AS "CAttending",
supervisor.clients_attended AS "CAttended",
MAX(ROUND((SYSDATE - supervisor_time_data.time_attending)*86400)) AS "MTA",
MAX(ROUND((SYSDATE - supervisor_time_data.time_waiting)*86400)) AS "MTE",
SUM(SYSDATE - supervisor_time_data.time_attending)*86400
/ supervisor.clients_attending AS "TMA",
SUM(SYSDATE - supervisor_time_data.time_waiting)*86400
/ supervisor.clients_waiting AS "TME",
supervisor.tme_accumulated AS "TME_ACCUMULATED",
supervisor.tma_accumulated AS "TMA_ACCUMULATED",
NVL(agenda.alarm_cee, 0) AS "ALARM_CEE",
NVL(agenda.alarm_mte, 0) AS "ALARM_MTE",
NVL(agenda.alarm_mta, 0) AS "ALARM_MTA",
NVL(agenda.alarm_tme, 0) AS "ALARM_TME"
FROM CORE.supervisor
LEFT JOIN CORE.supervisor_time_data
ON supervisor_time_data.id_service = supervisor.id_service
LEFT JOIN CORE.agenda ON supervisor.id_service = agenda.id
LEFT JOIN CORE.specialities ON agenda.idspeciality = specialities.id
WHERE supervisor.booked_or_sequential = 1
GROUP BY agenda.idagenda, agenda.name, supervisor.id_service, specialities.id,
specialities.name, supervisor.clients_waiting,
supervisor.clients_resent_waiting_area, supervisor.clients_attending,
supervisor.clients_attended, supervisor.tme_accumulated,
supervisor.tma_accumulated, agenda.alarm_cee,
agenda.alarm_mte,agenda.alarm_mta,agenda.alarm_tme;
So specifically, supervisor_time_data.time_waiting and supervisor_time_data.time_attending don't need to be in the group by as they are used in aggregate.
I've replaced your case checks with nvl just because it's shorter; case is fine though if you prefer that.

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

Formatting the output of oracle plsql query

I have a data set like this
id subid date(in yyyymmdd) time(in hh24miss) count1 count2
80013727 20000000431 20120429 001500 0 0
80013727 20000000431 20120429 003000 0 0
80013729 20000000432 20120429 001500 0 0
80013729 20000000432 20120429 003000 0 0
80013728 20000000435 20120429 001500 0 0
80013728 20000000435 20120429 003000 0 0
As you can see time is in 15 minutes increment . i want to show output the result set like below.
id Date subid 00:00:00-00:14:59 00:15:00-00:29:59
80013727 20120429 20000000431 0 0
80013729 20120429 20000000432 0 0
as you can see all all the data related to id 80013727 i s shown in one row instead of 2 for the date 20120429.
please tell me how to achieve it.
header row can be printed one time using dbms_output.put_line.
Hi here is your answers-
oracle ver 10.2 g
for a unique id,subid,date combination count1 and count2 is need to be shown in one row.
instead of 4 rows that can be seen from top most result set.
80013727 20000000431 20120429 has 2 rows for different time (i.e. 015000,030000)
I need to show
80013727 20000000431 20120429 count1(from 1st row),count1(from 2nd row)
80013727 20000000431 20120429 count2(from 1st row),count2(from 2nd row)
Obviously you have simplified your data and your output structure. I'm guessing you'll end up with 96 count columns (although I'm not going that far either).
with cte as
( select * from your_table )
select id
, subid
, date
, type
, sum(c01) as "00:00:00-00:14:59"
, sum(c02) as "00:15:00-00:29:59"
, sum(c96) as "23:45:00-23:59:59"
from (
select id
, subid
, date
, 'C1' type
, case when time between 0 and 899 then count1 else 0 end as c01
, case when time between 900 and 1799 then count1 else 0 end as c02
, case when time between 85500 and 86399 then count1 else 0 end as c96
from cte
union all
select id
, subid
, date
, 'C2' type
, case when time between 0 and 899 then count2 else 0 end as c01
, case when time between 900 and 1799 then count2 else 0 end as c02
, case when time between 85500 and 86399 then count2 else 0 end as c96
)
group by id, subid, date, type
order by id, subid, date, type
So, this use a sub-query factoring expression to select only once from your table. It uses case() to assign counts to a specific time column, on the basis of a range of seconds. There are two queries to aggregate the counts for lines 1 and 2.
The sum() calls may be unnecessary; it's not clear from your data whether you have more than one record in each time slot.

Resources