how combine multiple rows into a single row in Oracle? - oracle

How can i do below in oracle?
input
id units unit_description
12 20 kWh
12 50 kWh
12 100 days
15 80 kWh
I want merged in column 2 and 3 based on column 1 into one single row. And the result should be like
output:
id | unit details
12 70 KWh , 100 days
15 80 KWh

Check the below query. Mostly it should work in your case. It may not execute at once, modify as per your requirement.
SELECT id,
LISTAGG (unit || ' ' || unit_description, ', ')
WITHIN GROUP (ORDER BY unit, unit_description)
FROM ( SELECT id, unit_description, SUM (unit) AS unit
FROM table1
GROUP BY id, unit_description)
GROUP BY id;

Related

search in text using subtext in Oracle PLSQL

I have this query in master_t table that hold ration_category column and many number of sectors in sectors column like this 'BN:INS' or 'BN' or 'BN:IM:INS' etc..
select distinct ratio_category d from master_t
where status = 'Y' and (SECTORS = :P23_SECTORS or
(INSTR(':'||:SECTORS ||':',:P23_SECTORS)>0 OR UPPER(:P23_SECTORS) = 'ALL')) order by ratio_category
P23_SECTORS= 'BN:INS'
SECTORS='INS:MFI:SB:BN'
: is a separator of multi data
I need to check if data inside P23_sectors are exists in SECTORS variable ,
but this query doesn't get data because the multi data separator
Is there anyway to adjust the query so I could compare subtext with full text
note : the order is different.
Expected output :
LL
PP
CC
Thanks
Here's one option (read comments within code):
SQL> with master_t (ratio_category, sectors) as
2 -- sample data
3 (select 1, 'INS:MFI:SB:BN' from dual union all
4 select 2, 'BN:LF' from dual
5 ),
6 split_t as
7 -- split SECTORS into rows
8 (select ratio_category,
9 sectors,
10 regexp_substr(sectors, '[^:]+', 1, column_value) sec
11 from master_t cross join
12 table(cast(multiset(select level from dual
13 connect by level <= regexp_count(sectors, ':') + 1
14 ) as sys.odcinumberlist))
15 ),
16 split_23 as
17 -- split P23_SECTORS into rows
18 (select regexp_substr('&&P23_SECTORS', '[^:]+', 1, level) sec,
19 regexp_count('&&P23_SECTORS', ':') + 1 cnt
20 from dual
21 connect by level <= regexp_count('&&P23_SECTORS', ':') + 1
22 )
23 -- return rows that contain complete P23_SECTORS value(s)
24 select t.ratio_category, t.sectors
25 from split_t t join split_23 s on s.sec = t.sec
26 group by t.ratio_category, t.sectors
27 having count(*) = max(s.cnt);
Enter value for p23_sectors: BN:INS
RATIO_CATEGORY SECTORS
-------------- -------------
1 INS:MFI:SB:BN
SQL>
Pay attention to comments people posted. They do have serious experience with programming and you'd rather listen to what they say and thank them for spending time to check your problem. Your reply to Ed is rather rude; if I were Ed, I'd make sure not to respond to any of your future questions (read: I'd let you solve your own problems).

How to group by month including all months?

I group my table by months
SELECT TO_CHAR (created, 'YYYY-MM') AS operation, COUNT (id)
FROM user_info
WHERE created IS NOT NULL
GROUP BY ROLLUP (TO_CHAR (created, 'YYYY-MM'))
2015-04 1
2015-06 10
2015-08 22
2015-09 8
2015-10 13
2015-12 5
2016-01 25
2016-02 37
2016-03 24
2016-04 1
2016-05 1
2016-06 2
2016-08 2
2016-09 7
2016-10 103
2016-11 5
2016-12 2
2017-04 14
2017-05 2
284
But the records don't cover all the months.
I would like the output to include all the months, with the missing ones displayed in the output with a default value:
2017-01 ...
2017-02 ...
2017-03 ZERO
2017-04 ZERO
2017-05 ...
Oracle has a good array of date manipulation functions. The two pertinent ones for this problem are
MONTHS_BETWEEN() which calculates the number of months between two dates
ADD_MONTHS() which increments a date by the given number of months
We can combine these functions to generate a table of all the months spanned by your table's records. Then we use an outer join to conditionally join records from USER_INFO to that calendar. When no records match count(id) will be zero.
with cte as (
select max(trunc(created, 'MM')) as max_dt
, min(trunc(created, 'MM')) as min_dt
from user_info
)
, cal as (
select add_months(min_dt, (level-1)) as mth
from cte
connect by level <= months_between(max_dt, min_dt) + 1
)
select to_char(cal.mth, 'YYYY-MM') as operation
, count(id)
from cal
left outer join user_info
on trunc(user_info.created, 'mm') = cal.mth
group by rollup (cal.mth)
order by 1
/

oracle sql, counting working hours

I have been trying to find something related but couldn't.
I have an issue that i need to produce an availability percentage of something. I have a table that includes events that are happening, which i managed to count them by the day they are happening, but i am finding issues to count the total number of working hours in a quarter or a year.
when each day of the week has a different weight.
Basically my question is: can i do it without making a table with all dates in that month/year?
An example of the data:
ID DATE duration Environment
1 23/10/15 25 a
2 15/01/15 50 b
3 01/01/15 43 c
8 05/06/14 7 b
It can work for me by a calculated field or just a general query to get the information.
sorry I don't really understand the question but if you want to generate dates using connect by level is an easy way to do it (you could also use the model clause or recursive with) I did it here for just 10 days but you get the idea. I put in your dates as t1 and generated a list of dates (t) and then did a left outer join to put them together.
WITH t AS
(SELECT to_date('01-01-2015', 'mm-dd-yyyy') + level - 1 AS dt,
NULL AS duration
FROM dual
CONNECT BY level < = 10
),
t1 AS
(
SELECT to_date('10/01/15', 'dd-mm-yy') as dt, 50 as duration FROM dual
UNION ALL
SELECT to_date('01/01/15', 'dd-mm-yy'), 43 FROM dual
UNION ALL
SELECT to_date('06/01/15', 'dd-mm-yy'), 43 FROM dual
)
SELECT t.dt,
NVL(NVL(t1.duration, t.duration),0) duration
FROM t,
t1
WHERE t.dt = t1.dt(+)
ORDER BY dt
results
DT Duration
01-JAN-15 43
02-JAN-15 0
03-JAN-15 0
04-JAN-15 0
05-JAN-15 0
06-JAN-15 43
07-JAN-15 0
08-JAN-15 0
09-JAN-15 0
10-JAN-15 50
This was my intention, and the full answer below.
WITH t AS
(SELECT to_date('01-01-2015', 'mm-dd-yyyy') + level - 1 AS dt
FROM dual
CONNECT BY level < =365
),
t1 as
(
SELECT dt,
CASE
WHEN (to_char(TO_DATE( t.dt,'YYYY-MM-DD HH24:MI:SS'),'DY') in ('MON', 'TUE', 'WED', 'THU', 'FRI'))
THEN 14*60
WHEN (to_char(TO_DATE( t.dt,'YYYY-MM-DD HH24:MI:SS'),'DY') in ('SAT'))
THEN 8*60
WHEN (to_char(TO_DATE( t.dt,'YYYY-MM-DD HH24:MI:SS'),'DY') in ('SUN'))
THEN 10*60
ELSE 0 END duration ,
to_char(t.dt,'Q') as quarter
FROM t
)
select to_char(t1.dt,'yyyy'), to_char(t1.dt,'Q'),sum(t1.duration)
from t1
group by
to_char(t1.dt,'yyyy'), to_char(t1.dt,'Q');

select next record date minus 1 based on the logic

I want to achieve the following output through sql query
Below is the data in the oracle table
date 1 date 2 amt1 amt2
1/1/2012 12/31/2012 100 100
1/1/2013 11/31/2013 100 100
1/1/2014 9/31/2014 50 100
1/1/2015 12/31/2015 20 100
Desired output I need:
date 1 date 2 amt1 amt2
1/1/2012 12/31/2013 100 100
1/1/2014 12/31/2014 50 100
1/1/2015 12/31/9999 20 100
The logic to get the output would be if the amounts are same for multiple records then a single record should exist in the output with the earliest date as date1 . date 2 of the record written to the output should be next record effective date minus 1 , and if there is no next record found ,the date 2 should be defaulted to 31-dec-9999
(Note:whenever there is a change in amt1 or amt 2 between 2 records, those should be treated as separate records and if amts are same for all records then a single record should exist in the output )
Please let me know how to achieve this output through sql query
This solution uses a sub-query to aggregate a single row for each permutation of AMT1 and AMt2. Then it applies an analytic LEAD() function to get the next DATE1 and manipulates it according to the specification.
with yt as (
select amt1
, amt2
, min(date1) as date1
, max(date2) as date2
from your_table
group by amt1
, amt2
)
select yt.date1
, nvl(lead(yt.date1) over (order by yt.date1)-1, date '9999-12-31') as date2
, amt1
, amt2
from yt
order by yt.date1
/
By the way I had to correct your data (bad dates) when I used it in the inevitable SQL Fiddle; find it here.

How to find the difference between two timestamp column

I have table name Record which has the following columns, Empid in number column, dat in timestamp
Which has the following values
empid dat
====== ====
101 4/9/2012 9:48:54 AM
101 4/9/2012 9:36:28 AM
101 4/9/2012 6:16:28 PM
101 4/10/2012 9:33:48 AM
101 4/10/2012 12:36:28 PM
101 4/10/2012 8:36:12 PM
101 4/11/2012 9:36:28 AM
101 4/11/2012 4:36:22 PM
Here I need to display the following columns,
empid,min(dat) as start,max(dat) as end and difference(max(dat)-min(dat) for each day
Here 3 different days are exists so It should return 3 records with the above mentioned columns.
Please give some ways to get this.
Simply subtract them: max(dat) - min(dat)
SELECT empid,
min(dat) as strt,
max(dat) as end,
max(dat) - min(dat) as diff
FROM the_table
GROUP BY empid;
If you want to group by the day instead of the empid, use this one:
select trunc(dat),
min(dat) as strt,
max(dat) as end,
max(dat) - min(dat) as diff
from the_table
group by trunc(dat)
Date arithmetic is pretty straightforward in Oracle: the difference betwwen two dates is returned as the number of days. Values of less than a day are returned as *decimals". That is, 75 minutes is 1.25 hours not 1.15. If you want it as hours and minutes you need to work with an interval.
The inner query calculates the difference between the minimum and maximum data for each employee for each day, and converts it to a DAY interval. The outer query extracts the HOUR and MINUTES from that interval.
select empid
, tday
, sdt
, edt
, extract(hour from diff) diff_hours
, extract (minute from diff) diff_minutes
from (
select empid
, trunc(dat) tday
, min(dat) sdt
, max(dat) edt
, numtodsinterval(max(dat) - min(dat), 'DAY') diff
from t42
group by empid, trunc(dat)
)

Resources