oracle group dates rows by continous range - oracle

I need to group and sum the rows of one day according to continuous worker date range.
Table attendance definition:
row_no NUMBER (*,0) NOT NULL, -- row number - generated from a sequence
worker_id NUMBER NOT NULL, -- Attendance worker id
date1 DATE DEFAULT SYSDATE NOT NULL, -- Attendance Date/time
type1 NUMBER(3,0) NOT NULL, -- Attendance type: 0-Enter, 1-Exit
worker_id date1 type1
2 13/06/2016-09:00 0
3 13/06/2016-12:10 0
2 13/06/2016-13:20 1
2 13/06/2016-15:00 0
2 13/06/2016-17:00 1
3 13/06/2016-18:45 1
2 13/06/2016-19:00 0
Result if report is run at 22:00
worker_id date1 fr_hour to_hour hours
2 13/06/2016 09:00 13:20 4:20
2 13/06/2016 15:00 17:00 2:00
2 13/06/2016 19:00 22:00 3:00
3 13/06/2016 12:10 18:45 6:35

In the inner query we get for every row the date1,type1 from the next row (LEAD 1) for the same worker and than we filter only what we need:
SELECT worker_id,
TRUNC (date1) AS date1,
TO_CHAR (date1, 'HH24:MI') fr_hour,
TO_CHAR (date2, 'HH24:MI') to_hour,
TRUNC ( (date2 - date1) * 24) || ':' ||
TO_CHAR (TRUNC ( (date2 - date1) * 24 * 60) - TRUNC ( (date2 - date1) * 24) * 60, '00') hours
FROM (SELECT a.*,
LEAD (a.date1, 1) OVER (PARTITION BY worker_id ORDER BY date1) date2,
LEAD (a.type1, 1) OVER (PARTITION BY worker_id ORDER BY date1) type2
FROM testtemp a)
WHERE type1 = 0
AND type2 = 1
AND TRUNC (date1) = TRUNC (date2)

Taking continuous periods that start on an earlier day complicates it a bit. You can either calculate all the ranges etc. for all dates, back to the start of time - assuming you don't archive off old records and the very first entry for any worker in the data isn't a check-out - and then after doing all that work filter on the date you're interested in. Or you can look only at that date's data and see if a worker's records start with a check-in or check-out.
I've added records for a fourth worker:
WORKER_ID DATE1 TYPE1
---------- ---------------- ----------
4 2016-06-12 19:00 0
4 2016-06-13 03:00 1
2 2016-06-13 09:00 0
3 2016-06-13 12:10 0
4 2016-06-13 13:00 0
2 2016-06-13 13:20 1
4 2016-06-13 14:30 1
2 2016-06-13 15:00 0
2 2016-06-13 17:00 1
3 2016-06-13 18:45 1
4 2016-06-13 19:00 0
2 2016-06-13 19:00 0
You can use analytic functions to work out a row number for each entry, and also find the first type1 value for each worker that day; this also effectively pivots to get the time in and out as separate columns:
select worker_id, trunc(date1) as date1, type1,
case when type1 = 0 then date1 end as time_in,
case when type1 = 1 then date1 end as time_out,
row_number() over (partition by worker_id, trunc(date1), type1 order by date1) as rn,
min(type1) keep (dense_rank first order by date1) over (partition by worker_id, trunc(date1)) as open_start,
max(type1) keep (dense_rank last order by date1) over (partition by worker_id, trunc(date1)) as open_end,
row_number() over (partition by worker_id, trunc(date1), type1 order by date1)
- case when type1 = 1 then min(type1) keep (dense_rank first order by date1)
over (partition by worker_id, trunc(date1)) else 0 end as grp
from attendance
where date1 >= date '2016-06-13' and date1 < date '2016-06-14'
order by worker_id, attendance.date1;
WORKER_ID DATE1 TYPE1 TIME_IN TIME_OUT RN OPEN_START OPEN_END GRP
---------- ---------------- ---------- ---------------- ---------------- ---------- ---------- ---------- ----------
2 2016-06-13 00:00 0 2016-06-13 09:00 1 0 0 1
2 2016-06-13 00:00 1 2016-06-13 13:20 1 0 0 1
2 2016-06-13 00:00 0 2016-06-13 15:00 2 0 0 2
2 2016-06-13 00:00 1 2016-06-13 17:00 2 0 0 2
2 2016-06-13 00:00 0 2016-06-13 19:00 3 0 0 3
3 2016-06-13 00:00 0 2016-06-13 12:10 1 0 1 1
3 2016-06-13 00:00 1 2016-06-13 18:45 1 0 1 1
4 2016-06-13 00:00 1 2016-06-13 03:00 1 1 0 0
4 2016-06-13 00:00 0 2016-06-13 13:00 1 1 0 1
4 2016-06-13 00:00 1 2016-06-13 14:30 2 1 0 1
4 2016-06-13 00:00 0 2016-06-13 19:00 2 1 0 2
The rn column is a raw (naive) attempt to group in and out records together, but for worker 4 that goes out of step. The open_start works out if the first record was a check-out. The value that gets - either zero or 1 - can then be subtracted from rn to get a more useful grouping flag, which I've called grp.
You can then use that as an inline view or CTE and aggregate the time in/out records for each group, adding nvl() or coalesce() to put in missing midnight-start or 10pm-end values:
select worker_id,
date1 as date1,
nvl(min(time_in), date1) as fr_hour,
nvl(max(time_out), date1 + 22/24) as to_hour,
date1 + (nvl(max(time_out), date1 + 22/24) - date1)
- (nvl(min(time_in), date1) - date1) as hours
from (
select worker_id,
trunc(date1) as date1,
case when type1 = 0 then date1 end as time_in,
case when type1 = 1 then date1 end as time_out,
row_number() over (partition by worker_id, trunc(date1), type1 order by date1)
- case when type1 = 1 then min(type1) keep (dense_rank first order by date1)
over (partition by worker_id, trunc(date1)) else 0 end as grp
from attendance
where date1 >= date '2016-06-13' and date1 < date '2016-06-14'
)
group by worker_id, date1, grp
order by worker_id, date1, grp;
WORKER_ID DATE1 FR_HOUR TO_HOUR HOURS
---------- ---------------- ---------------- ---------------- ----------------
2 2016-06-13 00:00 2016-06-13 09:00 2016-06-13 13:20 2016-06-13 04:20
2 2016-06-13 00:00 2016-06-13 15:00 2016-06-13 17:00 2016-06-13 02:00
2 2016-06-13 00:00 2016-06-13 19:00 2016-06-13 22:00 2016-06-13 03:00
3 2016-06-13 00:00 2016-06-13 12:10 2016-06-13 18:45 2016-06-13 06:35
4 2016-06-13 00:00 2016-06-13 00:00 2016-06-13 03:00 2016-06-13 03:00
4 2016-06-13 00:00 2016-06-13 13:00 2016-06-13 14:30 2016-06-13 01:30
4 2016-06-13 00:00 2016-06-13 19:00 2016-06-13 22:00 2016-06-13 03:00
The 'hours' value manipulates the date and the time-in/out values to come up with what looks like another time; but it's actually the elapsed time.
Finally you can format the columns to remove the bits you aren't interested in:
select worker_id,
to_char(date1, 'DD/MM/YYYY') as date1,
to_char(nvl(min(time_in), date1), 'HH24:MI') as fr_hour,
to_char(nvl(max(time_out), date1 + 22/24), 'HH24:MI') as to_hour,
to_char(date1 + (nvl(max(time_out),date1 + 22/24) - date1)
- (nvl(min(time_in), date1) - date1), 'HH24:MI') as hours
from (
select worker_id,
trunc(date1) as date1,
case when type1 = 0 then date1 end as time_in,
case when type1 = 1 then date1 end as time_out,
row_number() over (partition by worker_id, trunc(date1), type1 order by date1)
- case when type1 = 1 then min(type1) keep (dense_rank first order by date1)
over (partition by worker_id, trunc(date1)) else 0 end as grp
from attendance
where date1 >= date '2016-06-13' and date1 < date '2016-06-14'
)
group by worker_id, date1, grp
order by worker_id, date1, grp;
WORKER_ID DATE1 FR_HO TO_HO HOURS
---------- ---------- ----- ----- -----
2 13/06/2016 09:00 13:20 04:20
2 13/06/2016 15:00 17:00 02:00
2 13/06/2016 19:00 22:00 03:00
3 13/06/2016 12:10 18:45 06:35
4 13/06/2016 00:00 03:00 03:00
4 13/06/2016 13:00 14:30 01:30
4 13/06/2016 19:00 22:00 03:00

Related

Oracle - Parent - child + fill mising hierarchy levels

I have created my fiddle example here: FIDDLE
Here is also athe code from the fiddle:
CREATE TABLE T1(ID INT, CODE INT, CODE_NAME VARCHAR(100), PARENT_ID INT);
INSERT INTO T1 VALUES(100,1,'LEVEL 1', NULL);
INSERT INTO T1 VALUES(110,11,'LEVEL 2', 100);
INSERT INTO T1 VALUES(120,111,'LEVEL 3', 110);
INSERT INTO T1 VALUES(125,112,'LEVEL 3', 110);
INSERT INTO T1 VALUES(130,1111,'LEVEL 4', 120);
INSERT INTO T1 VALUES(200,2,'LEVEL 1', NULL);
INSERT INTO T1 VALUES(210,21,'LEVEL 2', 200);
INSERT INTO T1 VALUES(300,3,'LEVEL 1', NULL);
I have trouble finding the soultuin how to get from that table this result:
| CODE | CODE NAME | CODE 1 |CODE NAME 1| CODE 2 | CODE NAME 2| CODE 3 | CODE NAME 3 |
+--------+------------+--------+-----------+--------+------------+--------+-------------+
| 1 | LEVEL 1 | 11 | LEVEL 2 | 111 | LEVEL 3 | 1111 | LEVEL 4 |
| 1 | LEVEL 1 | 11 | LEVEL 2 | 112 | LEVEL 3 | 112 | LEVEL 3 |
| 2 | LEVEL 1 | 21 | LEVEL 2 | 21 | LEVEL 2 | 21 | LEVEL 2 |
| 3 | LEVEL 1 | 3 | LEVEL 1 | 3 | LEVEL 1 | 3 | LEVEL 1 |
I have tried something with connect by but that is not what I need(I think)...
The max I will ever have is 4 levels and if there are only two levels in the data then the 3rd and the 4th level should be filled wiht the values of the last existing value. The same rule is valid if there are 3 levels or 1 level.
You can use a recursive sub-query:
WITH hierarchy (
code, code_name,
code1, code_name1,
code2, code_name2,
code3, code_name3,
id, depth
) AS (
SELECT code,
code_name,
CAST(NULL AS INT),
CAST(NULL AS VARCHAR2(100)),
CAST(NULL AS INT),
CAST(NULL AS VARCHAR2(100)),
CAST(NULL AS INT),
CAST(NULL AS VARCHAR2(100)),
id,
1
FROM t1
WHERE parent_id IS NULL
UNION ALL
SELECT h.code,
h.code_name,
CASE depth WHEN 1 THEN COALESCE(t1.code, h.code) ELSE h.code1 END,
CASE depth WHEN 1 THEN COALESCE(t1.code_name, h.code_name) ELSE h.code_name1 END,
CASE depth WHEN 2 THEN COALESCE(t1.code, h.code1) ELSE h.code2 END,
CASE depth WHEN 2 THEN COALESCE(t1.code_name, h.code_name1) ELSE h.code_name2 END,
CASE depth WHEN 3 THEN COALESCE(t1.code, h.code2) ELSE h.code3 END,
CASE depth WHEN 3 THEN COALESCE(t1.code_name, h.code_name2) ELSE h.code_name3 END,
t1.id,
h.depth + 1
FROM hierarchy h
LEFT OUTER JOIN t1
ON (h.id = t1.parent_id)
WHERE depth < 4
)
CYCLE code, depth SET is_cycle TO 1 DEFAULT 0
SELECT code, code_name,
code1, code_name1,
code2, code_name2,
code3, code_name3
FROM hierarchy
WHERE depth = 4;
Which, for the sample data:
CREATE TABLE T1(ID, CODE, CODE_NAME, PARENT_ID) AS
SELECT 100, 1, 'LEVEL 1', NULL FROM DUAL UNION ALL
SELECT 110, 11, 'LEVEL 2', 100 FROM DUAL UNION ALL
SELECT 120, 111, 'LEVEL 3', 110 FROM DUAL UNION ALL
SELECT 130, 1111, 'LEVEL 4', 120 FROM DUAL UNION ALL
SELECT 200, 2, 'LEVEL 1', NULL FROM DUAL UNION ALL
SELECT 210, 21, 'LEVEL 2a', 200 FROM DUAL UNION ALL
SELECT 220, 22, 'LEVEL 2b', 200 FROM DUAL UNION ALL
SELECT 230, 221, 'LEVEL 3', 220 FROM DUAL UNION ALL
SELECT 300, 3, 'LEVEL 1', NULL FROM DUAL;
Outputs:
CODE
CODE_NAME
CODE1
CODE_NAME1
CODE2
CODE_NAME2
CODE3
CODE_NAME3
1
LEVEL 1
11
LEVEL 2
111
LEVEL 3
1111
LEVEL 4
3
LEVEL 1
3
LEVEL 1
3
LEVEL 1
3
LEVEL 1
2
LEVEL 1
21
LEVEL 2a
21
LEVEL 2a
21
LEVEL 2a
2
LEVEL 1
22
LEVEL 2b
221
LEVEL 3
221
LEVEL 3
db<>fiddle here
For sample data you posted:
SQL> select * from t1;
ID CODE CODE_NAME PARENT_ID
---------- ---------- ---------- ----------
100 1 LEVEL 1
110 11 LEVEL 2 100
120 111 LEVEL 3 110
130 1111 LEVEL 4 120
200 2 LEVEL 1
210 21 LEVEL 2 200
6 rows selected.
SQL>
an ugly (and who-knows-how-performant) query that, though, returns desired result is
with temp as
(select id, code, code_name, parent_id, level lvl,
row_number() over (partition by level order by id) rn
from t1
start with parent_id is null
connect by prior id = parent_id
),
a as
(select * from temp where lvl = 1),
b as
(select * from temp where lvl = 2),
c as
(select * from temp where lvl = 3),
d as
(select * from temp where lvl = 4)
select
a.code code1, a.code_name code_name1,
coalesce(b.code, a.code) code2, coalesce(b.code_name, a.code_name) code_name2,
coalesce(c.code, b.code, a.code) code3, coalesce(c.code_name, b.code_name, a.code_name) code_name3,
coalesce(d.code, c.code, b.code, a.code) code4, coalesce(d.code_name, c.code_name, b.code_name, a.code_name) code_name4
from a join b on b.rn = a.rn
left join c on c.rn = b.rn
left join d on d.rn = c.rn;
which results in
CODE1 CODE_NAME1 CODE2 CODE_NAME2 CODE3 CODE_NAME3 CODE4 CODE_NAME4
---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
1 LEVEL 1 11 LEVEL 2 111 LEVEL 3 1111 LEVEL 4
2 LEVEL 1 21 LEVEL 2 21 LEVEL 2 21 LEVEL 2
What does it do?
temp CTE creates a hierarchy; additionally, row_number function numbers each row within the same level
a, b, c, d CTEs extract values belonging to their own level value (you said there can be up to 4 levels)
finally, coalesce on column names along with outer join do the job
From your example I assume you want to see one row per root key as your example is not realy a tree but a bamboo
If so this is a trivial PIVOT query - unfortunately limited to some level deep (here example for your 4 levels)
with p (ROOT_CODE, CODE, CODE_NAME, ID, PARENT_ID, LVL) as (
select CODE, CODE, CODE_NAME, ID, PARENT_ID, 1 LVL from t1 where PARENT_ID is NULL
union all
select p.ROOT_CODE, c.CODE, c.CODE_NAME, c.ID, c.PARENT_ID, p.LVL+1 from t1 c
join p on c.PARENT_ID = p.ID),
t2 as (
select ROOT_CODE, CODE,CODE_NAME,LVL from p)
select * from t2
PIVOT
(max(CODE) code, max(CODE_NAME) code_name
for LVL in (1 as "LEV1",2 as "LEV2",3 as "LEV3",4 as "LEV4")
);
ROOT_CODE LEV1_CODE LEV1_CODE_ LEV2_CODE LEV2_CODE_ LEV3_CODE LEV3_CODE_ LEV4_CODE LEV4_CODE_
---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
1 1 LEVEL 1 11 LEVEL 2 111 LEVEL 3 1111 LEVEL 4
2 2 LEVEL 1 21 LEVEL 2
The recursive CTE calculates the ROOT_CODE required for the pivot.
I' leaving as an exercise to fill the not defined levels (with COALESCE) with the previous values as in your example.
In case (as commented) you nedd oner row for each leave key a simple solution based on CONNECT_BY_PATHis possible.
I'm using again *recursive CTEcalculating the path from *root* to the *current node* and finaly filtering in the result the *leaves* (IDthat are notPARENT_ID`)
with p ( CODE, CODE_NAME, ID, PARENT_ID, PATH) as (
select CODE, CODE_NAME, ID, PARENT_ID, to_char(CODE)||'|'||CODE_NAME PATH from t1 where PARENT_ID is NULL
union all
select c.CODE, c.CODE_NAME, c.ID, c.PARENT_ID, p.PATH ||'|'||to_char(c.CODE)||'|'||c.CODE_NAME from t1 c
join p on c.PARENT_ID = p.ID)
select PATH from p
where ID in (select ID from T1 MINUS select PARENT_ID from T1)
order by 1;
The result holds for any level deepness and is concatenated string with delimiter
PATH
----------------------------------------------
1|LEVEL 1|11|LEVEL 2|111|LEVEL 3|1111|LEVEL 4
1|LEVEL 1|11|LEVEL 2|112|LEVEL 3
2|LEVEL 1|21|LEVEL 2
3|LEVEL 1
Use substr instr to extract and coalesce for the default values.
Solution using a hierarchical query - we record the code and code_name paths, then we break them apart. Level is used to decide whether we populate data from the paths or from the leaf node. The solution assumes the codes and code names do not contain the forward-slash character (if they may, use another separator in the paths - perhaps some control character like chr(31), the unit separator character in ASCII and Unicode).
To break apart the paths, I used regexp_substr as it's easier to work with (and, moreover, I assumed all codes and code names are non-null - if they may be null, the solution can be adapted easily). If this proves to be slow, that can be changed to use standard string functions.
with
p (code, code_name, parent_id, lvl, code_pth, code_name_pth) as (
select code, code_name, parent_id, level,
sys_connect_by_path(code, '/') || ',' ,
sys_connect_by_path(code_name, '/') || ','
from t1
where connect_by_isleaf = 1
start with parent_id is null
connect by parent_id = prior id
)
select case when lvl = 1 then code
else to_number(regexp_substr(code_pth, '[^/]+', 1, 1)) end as code,
case when lvl =1 then code_name
else regexp_substr(code_name_pth, '[^/]+', 1, 1) end as code_name,
case when lvl <= 2 then code
else to_number(regexp_substr(code_pth, '[^/]+', 1, 2)) end as code_1,
case when lvl <= 2 then code_name
else regexp_substr(code_name_pth, '[^/]+', 1, 2) end as code_name_1,
case when lvl <= 3 then code
else to_number(regexp_substr(code_pth, '[^/]+', 1, 3)) end as code_2,
case when lvl <= 3 then code_name
else regexp_substr(code_name_pth, '[^/]+', 1, 3) end as code_name_2,
code as code_3,
code_name as code_name_3
from p;

Oracle SQL Group By Hour for 24 hours

I have this query, which works:
SELECT TO_CHAR(last_date_called,'HH24'), count(*)
FROM log_table
GROUP BY TO_CHAR(last_date_called,'HH24');
But, in some cases there are not 24 hours worth of data. What I want to do, is always generate 24 rows, and if there is nothing for that hour, return 0. So, results may look like this:
00 10
01 25
02 33
03 0
04 55
05 0
06 23
And so on........
You'll need a row generator to create all hours in a day, and then outer join it to your "real" table. Something like this (see comments within code):
SQL> with
2 hours as
3 -- row generator, to create all hours in a day
4 (select lpad(level - 1, 2, '0') hour
5 from dual
6 connect by level <= 24
7 ),
8 log_table (last_date_called) as
9 -- sample data, just to return "something"
10 (select to_date('08.07.2021 13:32', 'dd.mm.yyyy hh24:mi') from dual union all
11 select to_date('16.02.2021 08:20', 'dd.mm.yyyy hh24:mi') from dual
12 )
13 -- final query
14 select h.hour,
15 count(l.last_date_called) cnt
16 from hours h left join log_table l on h.hour = to_char(l.last_date_called, 'hh24')
17 group by h.hour
18 order by h.hour;
HO CNT
-- ----------
00 0
01 0
02 0
03 0
04 0
05 0
06 0
07 0
08 1
09 0
10 0
11 0
12 0
13 1
14 0
15 0
16 0
17 0
18 0
19 0
20 0
21 0
22 0
23 0
24 rows selected.
SQL>

Kindly help to get office wise data row wise

select NUM_OFC_CODE,NUM_RO_CODE,
case when TXT_MONTH='JAN' then 1 ELSE 0 end as JAN,
case when TXT_MONTH='FEB' then 1 ELSE 0 end as FEB,
case when TXT_MONTH='MAR' then 1 ELSE 0 end as MAR,
case when TXT_MONTH='APR' then 1 ELSE 0 end as APR,
case when TXT_MONTH='MAY' then 1 ELSE 0 end as MAY,
case when TXT_MONTH='JUN' then 1 ELSE 0 end as JUN,
case when TXT_MONTH='JUL' then 1 ELSE 0 end as JUL,
case when TXT_MONTH='AUG' then 1 ELSE 0 end as AUG,
case when TXT_MONTH='SEP' then 1 ELSE 0 end as SEP,
case when TXT_MONTH='OCT' then 1 ELSE 0 end as OCT,
case when TXT_MONTH='NOV' then 1 ELSE 0 end as NOV,
case when TXT_MONTH='DEC' then 1 ELSE 0 end as DEC
from LEG_OMBUDSMAN_NONMACT where
NUM_YEAR=2019 group by NUM_OFC_CODE,TXT_MONTH,NUM_RO_CODE;
Result is showing as below:-
NUM_OFC_CODE NUM_RO_CODE JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC
280400 280000 0 0 0 0 0 0 0 1 0 0
282300 280000 0 0 0 0 0 0 0 1 0 0 0
281600 280000 0 0 0 0 0 0 0 1 0 0 0
280500 280000 0 0 0 0 0 0 1 0 0 0 0
280500 280000 0 0 0 1 0 0 0 0 0 0 0
281800 280000 0 0 0 0 0 0 0 1 0 0 0
282200 280000 0 0 0 0 0 0 0 1 0 0 0
280500 280000 0 0 0 0 1 0 0 0 0 0 0
280500 280000 0 0 0 0 0 1 0 0 0 0 0
280500 280000 0 0 0 0 0 0 0 1 0 0 0
281300 280000 0 0 0 0 0 0 0 1 0 0 0
I want office wise data. If August data is present, Then It should show 1 else 0. Like wise for other months. But in my query Separate row is showing for separate months.
Basically you have to group the data only by NUM_OFC_CODE,NUM_RO_CODE (excluding TXT_MONTH as you don't want a row for each instance of TXT_MONTH) and then use something like NVL(MAX(CASE WHEN TXT_MONTH='JAN' THEN 1 END), 0) as JAN (using a aggregate function to decide wether an entry exists or not) etc.
It's easier with the use of pivot:
-- Just some sampledata:
WITH LEG_OMBUDSMAN_NONMACT(NUM_OFC_CODE, NUM_RO_CODE, NUM_YEAR, TXT_MONTH) AS
(SELECT 1,1,2019, 'JAN' FROM dual union ALL
SELECT 1,1,2019, 'FEB' FROM dual)
-- Here starts the actual query:
SELECT NUM_OFC_CODE, NUM_RO_CODE
, NVL(JAN,0) AS JAN
, NVL(FEB,0) AS FEB
, NVL(MAR,0) AS MAR
, NVL(APR,0) AS APR
, NVL(MAY,0) AS MAY
, NVL(JUN,0) AS JUN
, NVL(JUL,0) AS JUL
, NVL(AUG,0) AS AUG
, NVL(SEP,0) AS SEP
, NVL(OCT,0) AS OCT
, NVL(NOV,0) AS NOV
, NVL(DEC,0) AS DEC
FROM LEG_OMBUDSMAN_NONMACT
pivot (MAX(1) FOR TXT_MONTH IN ('JAN' AS JAN,'FEB' AS FEB,'MAR' as MAR, 'APR' as APR, 'MAY' as MAY, 'JUN' as JUN, 'JUL' as JUL, 'AUG' as AUG, 'SEP' as SEP, 'OCT' as OCT, 'NOV' as NOV, 'DEC' as DEC ))
WHERE NUM_YEAR=2019
Your query is perfectly fine, just need couple of changes.
Remove txt_month from group by.
Use Max in all case statements.
So your query should look like this
select NUM_OFC_CODE,
NUM_RO_CODE,
Max(case when TXT_MONTH='JAN' then 1 ELSE 0 end) as JAN,
Max(case when TXT_MONTH='FEB' then 1 ELSE 0 end) as FEB,
Max(case when TXT_MONTH='MAR' then 1 ELSE 0 end) as MAR,
Max(case when TXT_MONTH='APR' then 1 ELSE 0 end) as APR,
Max(case when TXT_MONTH='MAY' then 1 ELSE 0 end) as MAY,
Max(case when TXT_MONTH='JUN' then 1 ELSE 0 end) as JUN,
Max(case when TXT_MONTH='JUL' then 1 ELSE 0 end) as JUL,
Max(case when TXT_MONTH='AUG' then 1 ELSE 0 end) as AUG,
Max(case when TXT_MONTH='SEP' then 1 ELSE 0 end) as SEP,
Max(case when TXT_MONTH='OCT' then 1 ELSE 0 end) as OCT,
Max(case when TXT_MONTH='NOV' then 1 ELSE 0 end) as NOV,
Max(case when TXT_MONTH='DEC' then 1 ELSE 0 end) as DEC
from LEG_OMBUDSMAN_NONMACT
where NUM_YEAR=2019
group by NUM_OFC_CODE ,NUM_RO_CODE;
Cheers!!

Oracle 11g - Adding a Total Column to a Pivot Table

I've created a pivot table with data from multiple tables (using JOINS). How can I add another column to the table which adds up each column from each row?
Example:
Category | A | B | C |
ABC 1 1 1
A 1 0 0
B 0 1 0
C 0 0 1
Category | A | B | C | TOTAL
ABC 1 1 1 3
A 1 0 0 1
B 0 1 0 1
C 0 0 1 1
SCOTT#research 15-APR-15> select * from testing ;
CATEG A B C
----- ---------- ---------- ----------
ABC 1 1 1
A 1 0 0
B 0 1 0
C 0 0 1
SCOTT#research 15-APR-15> select category,a,b,c, sum(a+b+c) as "total" from testing group by category,a,b,c order by category;
CATEG A B C total
----- ---------- ---------- ---------- ----------
A 1 0 0 1
ABC 1 1 1 3
B 0 1 0 1
C 0 0 1 1
In case you want to add a column, then can add one use a procedure to update the values using this,
alter table testing add total int;
use this procedure to update the values
create or replace procedure add_Test
is
sqlis varchar2(10);
total1 int;
begin
for i in (select * from testing) loop
select sum(a+b+c) into total1 from testing where category=i.category;
update testing set total=total1 where category=i.category;
end loop;
commit;
end;
exec add_test;
SCOTT#research 15-APR-15> select * from testing;
CATEG A B C TOTAL
----- ---------- ---------- ---------- ----------
ABC 1 1 1 3
A 1 0 0 1
B 0 1 0 1
C 0 0 1 1

Error while converting date string to date

In my project in my_table time of any event is stored in number format. Now I have to convert it to oracle datetime format.
Here is an explanation below:
Example1 :
•Sched_Arr_Tm = 0450, will equal 04:30 am (the first 2 is HH (24 hour clock. Since 04 < 12, then just use that number as the hour)) and the next 2 are the fractional equivalent of an hour (.50 * 60 min = 30 min)
•Sched_Arr_Tm = 2100, will equal 9:00 PM (since 21> 12, then take 21-12=9)
•Sched_Arr_Tm = 1475, will equal 02:45 Pm (the first 2 is HH (24 hour clock. Since 14 > 12. Then take 14-12=2), then just use that number as the hour)) and the next 2 are the fractional equivalent of an hour (.75 * 60 min = 45 min)
•Sched_Arr_Tm = 0075, will equal 12:45 AM (since the hour = 00, then the hour= 12) and the next 2 are the fractional equivalent of an hour (.75 * 60 min = 45 min)
I am able to extract data according to above login but getting error while converting it to date.
select sched_arr_tm,
LPAD(substr(tn.sched_arr_tm, 1,length(tn.sched_arr_tm) - 2),2,'0') as HH,
RPAD(TRUNC(TO_NUMBER(substr(tn.sched_arr_tm,3,length(tn.sched_arr_tm) - 2)) * .60,0),2,'0') as MM,
'00' AS SS,
LPAD(substr(tn.sched_arr_tm,1,length(tn.sched_arr_tm) - 2),2,'0')
||':' ||
RPAD(TRUNC(TO_NUMBER(substr(tn.sched_arr_tm,3,length(tn.sched_arr_tm) - 2)) * .60,0),2,'0')
||':'||
LPAD(0,2,0) AS DTTM,
TO_DATE(LPAD(substr(tn.sched_arr_tm,1,length(tn.sched_arr_tm) - 2),2,'0')
||':' ||
RPAD(TRUNC(TO_NUMBER(substr(tn.sched_arr_tm,3,length(tn.sched_arr_tm) - 2)) * .60,0),2,'0')
||':'||
LPAD(00,2,0),'HH24:MI:SS') AS DTTM,
tn.sched_slip_arr_tm
from MY_TABLE;
I am getting this error:
ORA-01858: a non-numeric character was found where a numeric was expected.
you can do this with:
SQL> with data as (select 450 Sched_Arr_Tm from dual
2 union all
3 select 1475 from dual
4 union all
5 select 2100 from dual)
6 select Sched_Arr_Tm, to_date(hours||':'||(60*(mins/100)), 'hh24:mi')
7 from (select Sched_Arr_Tm, substr(Sched_Arr_Tm, -2) mins,
8 substr(Sched_Arr_Tm, 1, length(Sched_Arr_Tm)-2) hours
9 from data)
10 /
SCHED_ARR_TM TO_DATE(HOURS||':
------------ -----------------
450 01-jan-2013 04:30
1475 01-jan-2013 14:45
2100 01-jan-2013 21:00
SQL>

Resources