Fix time issue in oracle - oracle

Table looks like below:
Expected answer below:
Write an oracle query to fix space and return hh:mm format. Consider same time in a same group and return total count

The WITH clause in the query below is just for testing; remove it, and use the actual table and column names in the main query. Note: count is a reserved keyword, so it can't be a column name. I changed it to count_ (with an underscore).
with
test_data (srt_tm, count_) as (
select '1:00' , 125 from dual union all
select '01:00' , 19000 from dual union all
select ' 01:00', 27 from dual union all
select '4:00' , 22000 from dual union all
select '04:00' , 1800 from dual union all
select ' 04:00', 15000 from dual
)
-- END OF TEST DATA; ACTUAL QUERY BEGINS **BELOW THIS LINE**
select lpad(trim(srt_tm), 5, '0') as srt_tm, sum(count_) as count_
from test_data
group by lpad(trim(srt_tm), 5, '0')
order by srt_tm
;
SRT_TM COUNT_
------ --------
01:00 19152
04:00 38800

Related

Compare date in Oracle when some value is null?

I want to get records that have an expired_date less than "31/12/2015", but some value of expired_date field is null in the database. This is my SQL:
"AND TO_DATE(EXPIRED_DATE,'DD/MM/YYYY') < TO_DATE('31/12/2015','DD/MM/YYYY')"
so I have got an error
"day of the month must between 1 and the last day of the month"
How can I get just records that have an expired_date < '31/12/2015'?
EDIT: The problem is not the null value, but my format in my DB. I store "22-APR-15" and mistake is trying to assign it to 'DD/MM/YYYY' by to_date function
If your column EXPIRED_DATE is of date type then, you don't need to convert it to date again using TO_DATE.
I think you need the following:
AND EXPIRED_DATE < TO_DATE('31/12/2015','DD/MM/YYYY')
This will return false for any EXPIRED_DATE which is null and that record will not be included in the result.
If you want Null EXPIRED_DATE to be included in the result then you can use OR as following:
AND (EXPIRED_DATE IS NULL OR EXPIRED_DATE < TO_DATE('31/12/2015','DD/MM/YYYY'))
Your code ...E(EXPIRED_DATE,'DD/MM/YYY')... has 3 YYY for date instead of 4 YYYY. Tried below and it works
with da(date_a) as (
select '03/04/2015' from dual
union all select '03/04/2015' from dual
union all select '03/04/2017' from dual
union all select '03/04/2015' from dual
union all select '03/04/2016' from dual
union all select NULL from dual
)
SELECT * FROM da WHERE TO_DATE(date_a,'DD/MM/YYYY') < TO_DATE('31/12/2015', 'DD/MM/YYYY');
Even when your date column is in date format it will still work
with da(date_a) as (
select '03/04/2015' from dual
union all select '03/04/2015' from dual
union all select '03/04/2017' from dual
union all select '03/04/2015' from dual
union all select '03/04/2016' from dual
union all select NULL from dual
)
SELECT to_date(date_a, 'DD/MM/YYYY') date_a FROM da WHERE TO_DATE(date_a,'DD/MM/YYYY') < TO_DATE('31/12/2015', 'DD/MM/YYYY');
db<>fiddle

How to convert this code from oracle to redshift?

I am trying to implement the same in redshift and i am finding it little difficult to do that. Since redshift is in top of postgresql engine, if any one can do it in postgresql it would be really helpfull. Basically the code gets the count for previous two month at column level. If there is no count for exact previous month then it gives 0.
This is my code:
with abc(dateval,cnt) as(
select 201908, 100 from dual union
select 201907, 200 from dual union
select 201906, 300 from dual union
select 201904, 600 from dual)
select dateval, cnt,
last_value(cnt) over (order by dateval
range between interval '1' month preceding
and interval '1' month preceding ) m1,
last_value(cnt) over (order by dateval
range between interval '2' month preceding
and interval '2' month preceding ) m2
from (select to_date(dateval, 'yyyymm') dateval, cnt from abc)
I get error in over by clause. I tried to give cast('1 month' as interval) but still its failing. Can someone please help me with this windows function.
expected output:
Regards
This is how I would do it. In Redshift there's no easy way to generate sequences, do I select row_number() from an arbitrary table to create a sequence:
with abc(dateval,cnt) as(
select 201908, 100 union
select 201907, 200 union
select 201906, 300 union
select 201904, 600),
cal(date) as (
select
add_months(
'20190101'::date,
row_number() over () - 1
) as date
from <an arbitrary table to generate a sequence of rows> limit 10
),
with_lag as (
select
dateval,
cnt,
lag(cnt, 1) over (order by date) as m1,
lag(cnt, 2) over (order by date) as m2
from abc right join cal on to_date(dateval, 'YYYYMM') = date
)
select * from with_lag
where dateval is not null
order by dateval

Grouping based on value by date range

Hi i have a data in daily basis below:
daytime value
01.01.2017 20000
02.01.2017 20000
03.01.2017 20000
04.01.2017 35000
05.01.2017 35000
06.01.2017 40000
07.01.2017 40000
08.01.2017 50000
How can i have in date range format such as below?
FromDate ToDate Value
01.01.2017 03.01.2017 20000
04.01.2017 05.01.2017 35000
06.01.2017 07.01.2017 40000
08.01.2017 08.01.2017 50000
Thanks!
Tabibitosan handles this very easily:
WITH your_table AS (SELECT to_date('01/01/2017', 'dd/mm/yyyy') daytime, 20000 VALUE FROM dual UNION ALL
SELECT to_date('02/01/2017', 'dd/mm/yyyy') daytime, 20000 VALUE FROM dual UNION ALL
SELECT to_date('03/01/2017', 'dd/mm/yyyy') daytime, 20000 VALUE FROM dual UNION ALL
SELECT to_date('04/01/2017', 'dd/mm/yyyy') daytime, 35000 VALUE FROM dual UNION ALL
SELECT to_date('05/01/2017', 'dd/mm/yyyy') daytime, 35000 VALUE FROM dual UNION ALL
SELECT to_date('06/01/2017', 'dd/mm/yyyy') daytime, 40000 VALUE FROM dual UNION ALL
SELECT to_date('07/01/2017', 'dd/mm/yyyy') daytime, 40000 VALUE FROM dual UNION ALL
SELECT to_date('08/01/2017', 'dd/mm/yyyy') daytime, 50000 VALUE FROM dual UNION ALL
SELECT to_date('09/01/2017', 'dd/mm/yyyy') daytime, 20000 VALUE FROM dual)
-- end of mimicking your table with data in it. See SQL below:
SELECT MIN(daytime) fromdate,
MAX(daytime) todate,
VALUE
FROM (SELECT daytime,
VALUE,
row_number() OVER (ORDER BY daytime) - row_number() OVER (PARTITION BY VALUE ORDER BY daytime) grp
FROM your_table)
GROUP BY grp,
VALUE
ORDER BY MIN(daytime);
FROMDATE TODATE VALUE
---------- ---------- ----------
01/01/2017 03/01/2017 20000
04/01/2017 05/01/2017 35000
06/01/2017 07/01/2017 40000
08/01/2017 08/01/2017 50000
09/01/2017 09/01/2017 20000
What this does is compare the row number for all the rows ordered by date, and then the row number for all the rows for each value ordered by date. If the value rows are consecutive in the main set of data, then the difference between the two sets of data remains the same, so you can then group by that. If there is a gap, then the difference increases.
In your example above, the first three rows for value = 20000 happen to be the first three rows of the whole set, so the difference will be 0. However the fourth value = 20000 row is the 9th row in the whole set, so the difference is now 5. You can easily see that the value of 20000 falls into two groups, and as such, you can find the min/max daytime for each group separately by including that difference calculation in the group by clause.
N.B. This does assume that the dates in your data are consecutive or that if there are missing dates that you assume the value stays the same for the missing dates. If you do have missing days and you want the values across a gap to show in different groups, you'd need to outer join to a subquery that contains the missing dates. In that case, I think GurV's answer (with the additional clause in the case statement that I mentioned in the comments) would be the best one to use, as that would avoid the need to outer join to a list of consecutive dates.
If I understand correctly, you want to group the value only if they are same for consecutive dates.
You can use window functions to generate groups based on value and increasing date order and then find the required aggregates.
with your_table(daytime ,value) as (
select to_date('13.02.2017','dd.mm.yyyy'),25000 from dual union all
select to_date('14.02.2017','dd.mm.yyyy'),20000 from dual union all
select to_date('15.01.2017','dd.mm.yyyy'),90000 from dual union all
select to_date('16.01.2017','dd.mm.yyyy'),90000 from dual union all
select to_date('17.01.2017','dd.mm.yyyy'),95800 from dual union all
select to_date('18.01.2017','dd.mm.yyyy'),95800 from dual union all
select to_date('19.01.2017','dd.mm.yyyy'),95800 from dual union all
select to_date('20.01.2017','dd.mm.yyyy'),95800 from dual union all
select to_date('21.01.2017','dd.mm.yyyy'),95800 from dual union all
select to_date('22.01.2017','dd.mm.yyyy'),95800 from dual union all
select to_date('23.01.2017','dd.mm.yyyy'),95800 from dual union all
select to_date('24.01.2017','dd.mm.yyyy'),90000 from dual union all
select to_date('25.01.2017','dd.mm.yyyy'),90000 from dual union all
select to_date('26.01.2017','dd.mm.yyyy'),90000 from dual
)
select
min(daytime) fromdate,
max(daytime) todate,
value
from (
select
t.*,
sum(x) over (order by daytime) grp
from (
select
t.*,
case when value = lag(value) over (order by daytime)
then 0 else 1 end x
from your_table t
) t
) t group by grp, value
order by fromdate;
Produces:
FROMDATE TODATE VALUE
15-JAN-17 16-JAN-17 90000
17-JAN-17 23-JAN-17 95800
24-JAN-17 26-JAN-17 90000
13-FEB-17 13-FEB-17 25000
14-FEB-17 14-FEB-17 20000

Grouping and aggregation based on a specific condition

My result set from a query looks like this
trackingnumber type price
------------------------------------------
12799467 AVRM 674.0536
12799467 AVRM 860.7415
12799467 PRICESTD 200.00
12799468 PRICESTD 590.85
12799469 PRICESTD 800
12799470 PRICESTD 640
12799471 PRICESTD 160
12799472 PRICESTD 2080
12799473 PRICESTD 354.3779
I want to group this by the trackingnumber and in cases where the count of grouped result set is greater than 1 return the SUM of all the price which has type as AVRM else return the individual price as it is. If the count
is more that zero and the none of them has type AVRM then it's total price would be null
The expected result would be this
trackingnumber Total Price
-----------------------------------------
12799467 1534.7951 --sum of price excluding 200
12799468 590.85
12799469 800
12799470 640
12799471 160
12799472 2080
12799473 354.3779
I couldn't think of a way to get this done except for grouping by trackingnumber and checking for type by using case statement in the select part but that I believe would not work since we do not group by type
I'm not sure if this can be achieved using a single query.
Yes, it can be done in a single query.
with sample_data ( tracking_Number, "TYPE", price ) as
(
SELECT 12799467,'AVRM',674.0536 FROM DUAL UNION ALL
SELECT 12799467,'AVRM',860.7415 FROM DUAL UNION ALL
SELECT 12799467,'PRICESTD',200.00 FROM DUAL UNION ALL
SELECT 12799468,'PRICESTD',590.85 FROM DUAL UNION ALL
SELECT 12799469,'PRICESTD',800 FROM DUAL UNION ALL
SELECT 12799470,'PRICESTD',640 FROM DUAL UNION ALL
SELECT 12799471,'PRICESTD',160 FROM DUAL UNION ALL
SELECT 12799472,'PRICESTD',2080 FROM DUAL UNION ALL
SELECT 12799473,'PRICESTD',354.3779 FROM DUAL )
SELECT tracking_number,
case when count(*) > 1 THEN
sum(decode("TYPE",'AVRM',price,null)) ELSE
sum(price) END price
from sample_data
group by tracking_number
order by tracking_Number;
For example next solution. I add condition to exlude rows with type not equla 'AVRM' if rows with 'AVRM' exests
with s (trackingnumber ,type ,price)
as (
select 12799467,'AVRM',674.0536 from dual union all
select 12799467 ,'AVRM', 860.7415 from dual union all
select 12799467 ,'PRICESTD', 200.00 from dual union all
select 12799468 ,'PRICESTD', 590.85 from dual union all
select 12799469 ,'PRICESTD', 800 from dual union all
select 12799470 ,'PRICESTD', 640 from dual union all
select 12799471 ,'PRICESTD', 160 from dual union all
select 12799472 ,'PRICESTD', 2080 from dual union all
select 12799473 ,'PRICESTD', 354.3779 from dual )
select trackingnumber,
sum(price)
from (select s.*,rownum as rn from s
where not exists (select null
from s subs
where s.trackingnumber = subs.trackingnumber
and s.type != 'AVRM'
and subs.type = 'AVRM')
)
group by trackingnumber,
case when type = 'AVRM' then 0 else rn end;

Select Maximum record

Here is my Table EMP_EARN_DETAILS.
Emp_Ern_No is the primary key.
I need to get the amount for each emp_no for each earn_no where the emp_earn_no is the maximum.
The output should be as follows.
0004321 ERN001 2345 11
0004321 ERN002 345 10
0004321 ERN003 345 9
000507 ER-01 563 4
000732 ERN001 2345 12
000732 ERN002 9 13
000732 ERN003 678 8
Please help me with the query
You can aggregate by the fields you need and, at the same time, order by the EMP_EARN_NO value; this can be a solution, by analytic functions:
WITH TEST(emp_no, earn_no, amount, emp_earn_no) AS
(
SELECT '0004321' , 'ERN001' ,2345 ,11 FROM DUAL UNION ALL
SELECT '0004321' , 'ERN002' ,345 , 10 FROM DUAL UNION ALL
SELECT '0004321' , 'ERN003' ,345 ,9 FROM DUAL UNION ALL
SELECT '000507' , 'ER-01' ,56 ,1 FROM DUAL UNION ALL
SELECT '000507' , 'ER-01' ,563 , 2 FROM DUAL UNION ALL
SELECT '000507' , 'ER-01' ,563 ,3 FROM DUAL UNION ALL
SELECT '000507' , 'ER-01' ,563 ,4 FROM DUAL UNION ALL
SELECT '00732' , 'ERN001' ,123 ,7 FROM DUAL UNION ALL
SELECT '00732' , 'ERN001' ,2345 ,12 FROM DUAL UNION ALL
SELECT '00732' , 'ERN002' ,9 ,13 FROM DUAL UNION ALL
SELECT '00732' , 'ERN003' ,67 ,5 FROM DUAL UNION ALL
SELECT '00732' , 'ERN003' ,456 ,6 FROM DUAL UNION ALL
SELECT '00732' , 'ERN003' ,678 ,8 FROM DUAL
)
SELECT emp_no, earn_no, amount, emp_earn_no
FROM (
SELECT emp_no,
earn_no,
amount,
emp_earn_no, ROW_NUMBER() OVER ( PARTITION BY EMP_NO, EARN_NO ORDER BY emp_earn_no DESC) AS ROW_NUM
FROM TEST
)
WHERE ROW_NUM = 1
Give this a shot,
SELECT EMP_NO, SUM(AMOUNT)
FROM EMP_EARN_DETAILS
GROUP BY EMP_NO
HAVING EMP_EARN_NO = MAX(EMP_EARN_NO)
Try this query:
select emp_no, earn_no,
sum(amount) keep (dense_rank last order by emp_earn_no) as sum_amount
from emp_earn_details
group by emp_no, earn_no
First by following query , your conditions achieved :
select t.emp_no a ,t.earn_no b ,max(t.amount) c
from EMP_EARN_DETAILS t
group by t.emp_no,t.earn_no
order by t.emp_no
Only things that you must specify , in a same record with different EMP_EARN_NO. You have to specify in same record which must be in result.
So if you want maximum EMP_EARN_NO be in result you can use following query as final query (exactly your target in question):
select t.emp_no a ,t.earn_no b ,max(t.amount) c, max(t.emp_earn_no) emp_earn_no
from EMP_EARN_DETAILS t
group by t.emp_no,t.earn_no
order by t.emp_no
If you want minimum or others EMP_EARN_NO be in result you can above query replace max function by your conditions.

Resources