I need help writing a aging report on oracle.
The report should be like:
aging file to submit total 17
aging file to submit 0-2 days 3
aging file to submit 2-4 days 4
aging file to submit 4-6 days 4
aging file to submit 6-8 days 2
aging file to submit 8-10 days 4
I can create a query for each section and then union all the the results like:
select 'aging file to submit total ' || count(*) from FILES_TO_SUBMIT where trunc(DUE_DATE) > trunc(sysdate) -10
union all
select 'aging file to submit 0-2 days ' || count(*) from FILES_TO_SUBMIT where trunc(DUE_DATE) <= trunc(sysdate) and trunc(DUE_DATE) >= trunc(sysdate-2)
union all
select 'aging file to submit 2-4 days ' || count(*) from FILES_TO_SUBMIT where trunc(DUE_DATE) <= trunc(sysdate-2) and trunc(DUE_DATE) >= trunc(sysdate-4) ;
I was wondering if there is a better way using oracle analytic functions or any other query that will get better performance?
Sample data:
CREATE TABLE files_to_submit(file_id int, file_name varchar(255),due_date date);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 1, 'file_' || 1, sysdate);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 2, 'file_' || 2, sysdate -5);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 3, 'file_' || 3, sysdate -4);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 4, 'file_' || 4, sysdate);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 5, 'file_' || 5, sysdate-3);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 6, 'file_' || 6, sysdate-7);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 7, 'file_' || 7, sysdate-10);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 8, 'file_' || 8, sysdate-12);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 9, 'file_' || 9, sysdate-3);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 10, 'file_' || 10, sysdate-5);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 11, 'file_' || 11, sysdate-6);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 12, 'file_' || 12, sysdate-7);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 13, 'file_' || 13, sysdate-5);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 14, 'file_' || 14, sysdate-4);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 15, 'file_' || 15, sysdate-2);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 16, 'file_' || 16, sysdate-6);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 17, 'file_' || 17, sysdate-6);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 18, 'file_' || 18, sysdate-5);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 19, 'file_' || 19, sysdate-10);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 20, 'file_' || 20, sysdate-9);
DROP TABLE files_to_submit;
you can use this simple approach to get the report for all days(without total):
select
'aging file to submit '|| trunc(dist/2)*2 ||'-'|| (trunc(dist/2)*2+2) || ' days: ' || count(*)
from (
select trunc(sysdate) - trunc(DUE_DATE) as dist
from FILES_TO_SUBMIT
--where trunc(DUE_DATE) > trunc(sysdate) -10
)
group by trunc(dist/2)
order by trunc(dist/2);
The only thing that is important is just number of days (dist(ance) field).
If you want to have also the Total in the same scan:
select
'aging file to submit '||
case
when trunc(dist/2) is null
then 'Total '
else trunc(dist/2)*2 ||'-'|| (trunc(dist/2)*2+2) || ' days: '
end ||
count(*)
from (
select trunc(sysdate) - trunc(DUE_DATE) as dist
from FILES_TO_SUBMIT
where trunc(DUE_DATE) > trunc(sysdate) -10
)
group by rollup(trunc(dist/2))
order by trunc(dist/2)
nulls first;
Hint: If you have hundreds of days of history an index would be useful. (pay attention: if your table is very big, >100Milion, the creation of the index will take some time)
create index index_name on files_to_submit(due_date);
and then change the condition to:
where DUE_DATE > trunc(sysdate) - 10
This will speed up y
Allow me to suggest WIDTH_BUCKET.
This will divide the date range into equal size. Since you want 10 days range into groups of 2 days, the bucket size will be 10 / 2 = 5.
Query:
SELECT
CASE GROUPING(bucket)
WHEN 1
THEN 'aging file to submit Total'
ELSE 'aging file to submit ' || (bucket-1)*2 || '-' || (bucket)*2 || ' days'
END AS bucket_number,
COUNT(1) AS files
FROM (
SELECT
WIDTH_BUCKET(due_date, sysdate, sysdate-10, 5) bucket
FROM
files_to_submit
WHERE
due_date >= sysdate-10
)
GROUP BY
ROLLUP(bucket)
ORDER BY
bucket NULLS FIRST;
Result:
BUCKET_NUMBER FILES
------------------------------------ ----------
aging file to submit Total 17
aging file to submit 0-2 days 2
aging file to submit 2-4 days 3
aging file to submit 4-6 days 6
aging file to submit 6-8 days 5
aging file to submit 8-10 days 1
I got different counts using your sample data -- I get 19 total instead of 17 (which seems appropriate as only one of the 20 records in your sample data is out of the range):
WITH d1 AS (
SELECT 2 AS day_cnt FROM dual
UNION ALL
SELECT 4 FROM dual
UNION ALL
SELECT 6 FROM dual
UNION ALL
SELECT 8 FROM dual
UNION ALL
SELECT 10 FROM dual
)
SELECT NVL(title, 'aging file to submit total') AS title, COUNT(DISTINCT file_id)
FROM (
SELECT 'aging file to submit ' || prev_day || '-' || day_cnt || ' days' AS title, f1.file_id
FROM (
SELECT day_cnt, NVL(LAG(day_cnt) OVER ( ORDER BY day_cnt ), 0) AS prev_day
FROM d1
) d2, files_to_submit f1
WHERE TRUNC(f1.due_date) <= TRUNC(SYSDATE - d2.prev_day)
AND TRUNC(f1.due_date) >= TRUNC(SYSDATE - d2.day_cnt)
) GROUP BY ROLLUP(title);
Also, the counts for the day ranges aren't right (they don't add up to 19, that is) because of the the files can be counted twice due to the use of TRUNC() and including both end cases. But I'm sure you can tweak the above to give what you want.
WITH r (
'aging file to submit ' Prefix,
Total,
Days0_2,
Days2_4,
Days4_6,
Days6_8,
Days8_10
) AS (
SELECT
SUM(Total) Total,
SUM(Days0_2) Days0_2,
SUM(Days2_4) Days2_4,
SUM(Days4_6) Days4_6,
SUM(Days6_8) Days6_8,
SUM(Days8_10) Days8_10
FROM (
SELECT
(CASE WHEN f.days <= 2 THEN f.num ELSE NULL END) AS Days0_2,
(CASE WHEN f.days >= 2 AND f.days <= 4 THEN f.num ELSE NULL END) AS Days2_4,
(CASE WHEN f.days >= 4 AND f.days <= 6 THEN f.num ELSE NULL END) Days4_6,
(CASE WHEN f.days >= 6 AND f.days <= 8 THEN f.num ELSE NULL END) AS Days6_8,
(CASE WHEN f.days >= 8 AND f.days <= 10 THEN f.num ELSE NULL END) AS Days8_10,
f.num AS Total
FROM (
SELECT
COUNT(*) AS num,
TRUNC(due_date) - TRUNC(SYSDATE) + 10 AS days
FROM FILES_TO_SUBMIT t
WHERE (TRUNC(due_date) - TRUNC(SYSDATE) + 10) >= 0
GROUP BY TRUNC(due_date) - TRUNC(SYSDATE) + 10
) f
) s
)
SELECT Prefix || 'Total' AS Label, Total AS Count FROM r
UNION ALL SELECT Prefix || '0-2 days', Days0_2 FROM r
UNION ALL SELECT Prefix || '2-4 days', Days2_4 FROM r
UNION ALL SELECT Prefix || '4-6 days', Days4_6 FROM r
UNION ALL SELECT Prefix || '6-8 days', Days6_8 FROM r
UNION ALL SELECT Prefix || '8-10 days', Days8_10 FROM r
It will not double count records for the Total row. Since your day ranges overlap you cannot sum up the individual counts to get the total. As another query given here gives a total of 25 with only 20 records and 1 out of range.
The result for total is what you expect with 20 records and 1 being 12 days old. The innermost query does all the heavy lifting but it is executed once to get all aging results. Its result will be at most 11 rows, 0-10 days. The rest of the queries are for final results and pretty output.
You can eliminate one query level by SUMing one level in, I just find it easier to validate the results by being able to select intermediate queries for a spot check.
Here is the result of the query:
This approach allows you to maintain your buckets separately from your main SQL in case you want them to be different sizes or to name them something that is not generated from the SQL, such as 'On time', 'Delinquent', etc. and also provides a very readable main SQL block.
with aging as
(select count(*) count_per_day, (trunc(sysdate) - trunc(f.due_date)) age
from files_to_submit f
where trunc(f.due_date - 10) <= sysdate
group by (trunc(sysdate) - trunc(f.due_date))),
buckets as
(select 1 bucket_id, 0 bucket_min, 2 bucket_max, 'aging file to submit 0-2' bucket_name from dual
union select 2, 2, 4, 'aging file to submit 2-4' from dual
union select 3, 4, 6, 'aging file to submit 4-6' from dual
union select 4, 6, 8, 'aging file to submit 6-8' from dual
union select 5, 8, 10, 'aging file to submit 8-10' from dual
union select 6, null, null, 'aging file to submit total' from dual
)
select nvl(b.bucket_name, (select bucket_name from buckets where bucket_id = 6)), sum(a.count_per_day) bucket_cnt
from aging a
join buckets b on (a.age >= b.bucket_min and a.age <= b.bucket_max)
group by rollup(b.bucket_name)
order by b.bucket_name nulls first;
Related
I need to find anyone who was >=6 or <18 per month in 2022.
so for example...even if someone was five on 1/15/2022 and then turned 6 on 1/16/2022 will be counted. or if someone was 17 on 1/20/22 and then turned 18 on 1/21/2022 will still be counted.
im relatively new to sql so any help would be appreciated.
The dates are german format, but you should adapt the logic:
DECLARE #table Table
(kd_id int , birthdate DATE)
INSERT INTO #table
(
kd_id,
birthdate
)
SELECT 1, '01.10.2016'
UNION
SELECT 2, '09.10.2016'
UNION
SELECT 3, '10.10.2016'
UNION
SELECT 4, '11.10.2016'
UNION
SELECT 5, '17.10.2016'
UNION
SELECT 6, '01.10.2004'
UNION
SELECT 7, '09.10.2004'
UNION
SELECT 8, '10.10.2004'
UNION
SELECT 9, '11.10.2004'
UNION
SELECT 10, '17.10.2004'
UNION
SELECT 11, '01.01.2016'
UNION
SELECT 12, '01.01.2004'
UNION
SELECT 13, '01.01.2017'
UNION
SELECT 14, '31.12.2003'
UNION
SELECT 15, '31.12.2003'
UNION
SELECT 16, '31.10.2004'
UNION
SELECT 17, '30.09.2004'
UNION
SELECT 18, '01.11.2016'
--Query
DECLARE #month NVARCHAR(2) = '10' --October
,#year NVARCHAR(4) = '2022'
,#month_begin NVARCHAR(10)
,#month_end NVARCHAR(10)
SET #month_begin = '01.' + #month + '.2022'
SET #month_end = EOMONTH(#month_begin) --EOMONTH() = Last date of a month
SELECT kd_id
,birthdate
FROM #table
WHERE DATEDIFF(hour,birthdate,#month_begin)/8766 BETWEEN 6 AND 17
OR DATEDIFF(hour,birthdate,#month_end)/8766 BETWEEN 6 AND 17
Long story short, I was handed this query (edited a bit for the forum world) to get it working in order to start creating reports. Having some issues with the 3 CASE statements (tried to add comment within the code to explain a little better what is happening). All data is being pulled from the same table/view.
I know I need to create some nested SELECT statements in order to make it work, but I'm struggling. What I'm trying to have happen is...
CASE 2 to only run if CASE 1 IS NOT NULL, where the column would be a result of (amd_month + 1 month). Format being YYYYMM
CASE 3 to only run if CASE 2 IS NOT NULL, where the column would be a result of (ext_srg_start_date + 6 months). Format being YYYYMM
SELECT
part,
amd_month,
amd actual_demand,
round(roll_12amd * 1.15) srg_lmt,
round(roll_12amd * 1.5) ext_srg_lmt,
-- CASE: 1
-- calculate level- COMPLETE
CASE
WHEN (amd > roll_12amd * 1.5) THEN 'Extreme Surge'
WHEN (amd > roll_12amd * 1.15) THEN 'Surge'
ELSE NULL
END AS srg_nt,
-- CASE: 2
-- if amd_month = 12, (+1 year to) and (-11 from month) from ext_srg_start_date,
-- else +1 month to ext_srg_start_date - COMPLETE
CASE
WHEN substr(amd_month, 5, 2) IN (
'12'
) THEN concat(TO_CHAR(substr(amd_month, 1, 4) + 1), lpad(TO_CHAR(substr(amd_month, 5, 2) - 11), 2, '0'))
ELSE concat(substr(amd_month, 1, 4), lpad(TO_CHAR(substr(amd_month, 5, 2) + 1), 2, '0'))
END AS ext_srg_start_date,
---- CASE: 3
---- +6 to ext_srg_start_date - IN PROGRESS
CASE
WHEN substr(amd_month, 5, 2) IN (
'01',
'02',
'03',
'04',
'05',
'06'
) THEN concat(substr(amd_month, 1, 4), lpad(TO_CHAR(substr(amd_month, 5, 2) + 6), 2, '0'))
ELSE concat(TO_CHAR(substr(amd_month, 1, 4) + 1), lpad(TO_CHAR(substr(amd_month, 5, 2) - 6), 2, '0'))
END AS ext_srg_carry_date
FROM
connect.table
Any help would be awesome. Thanks!
You can do away with case statements 2 and 3 if AMD_Month is a date data type, you can also do away with them if it's not but it takes an extra step:
If AMD_Month is a Date column then ext_srg_start_date and ext_srg_carry_date could be defined as:
...
, AMD_Month + INTERVAL '1' MONTH ext_srg_start_date
, AMD_Month + INTERVAL '6' MONTH ext_srg_carry_date
...
If AMD_Month is a string, then you first need to convert it to a date and I guess then convert the result back to a string:
...
, TO_CHAR( TO_DATE( AMD_Month, 'YYYYMM')
+ INTERVAL '1' MONTH, 'YYYYMM') ext_srg_start_date
, TO_CHAR( TO_DATE( AMD_Month, 'YYYYMM')
+ INTERVAL '6' MONTH, 'YYYYMM') ext_srg_carry_date
...
Date arithmatic works so much better when you store date data types as dates or timestamps than when you store them as strings.
I need to get the date and day count values between two dates.
let's suppose, we want to get the records between 1 july to 5 August.
the output should be like below Table image:
we already know that we have 5 weeks in july month and 1 week in august month for this date range :
let's start with first week to last week :
Try this Goutam Singh. I think daycount should now be 7, 2 using the monday as start of the week.
SELECT
[DYear],
[DMonth],
[Week],
DayCount=COUNT(DISTINCT DayCount),
BillableHour=SUM(BillableHour)
FROM
(
SELECT
[DYear]=(YEAR(Workdate)) ,
[DMonth]=(DATENAME(MONTH, Workdate)) ,
DateNames=datename(dw, Workdate),
[Week]='Week ' + CAST((DATEPART(wk,DATEADD(DAY, -1,Workdate)) - MAX(DATEPART(wk,DATEADD(DAY, -1,Workdate)) )over(partition by (select null))+2) AS varchar(20)),
DayCount= ( WorkDate),
BillableHour=(Convert(DECIMAL(16,2),[Hours]))
FROM
#TempTable
WHERE
Workdate between CONVERT(datetime,#FromDate) and CONVERT(datetime,#ToDate)
)G
GROUP BY
[DYear],
[DMonth],
[Week]
Number of days in a week column is not clear to me. otherwise, below will be the query. Just replace value and date columns with your appropriate column names.
select count(value),month(date),datepart(WEEKDAY,date()) as number of week,date
from t
group by date
#Goutam Singh I have here an updated version. Basically, you need a CTE to build a template table for your query and then do a join depending on what is in the table you want to get the TotalHours. Let me know if that is what you want.
DECLARE #StartDate DATE='20180101'
DECLARE #EndDate DATE='20180901'
DECLARE #Dates TABLE(
Workdate DATE Primary Key
)
DECLARE #TempTable TABLE (Id INT, Hours real, WorkDate DATETIME )
INSERT INTO #TempTable
SELECT 1, 5, '03.05.2018 00:00:00' UNION ALL
SELECT 2, 1.5, '08.05.2018 00:00:00' UNION ALL
SELECT 3, 3, '01.05.2018 00:00:00' UNION ALL
SELECT 4, 0, '04.05.2018 00:00:00' UNION ALL
SELECT 5, 2, '03.05.2018 00:00:00' UNION ALL
SELECT 6, 4, '03.05.2018 00:00:00' UNION ALL
SELECT 7, 2, '05.05.2018 00:00:00' UNION ALL
SELECT 8, 0.5, '08.05.2018 00:00:00' UNION ALL
SELECT 9, 0, '01.05.2018 00:00:00' UNION ALL
SELECT 10, 6, '08.05.2018 00:00:00' UNION ALL
SELECT 11, 8, '02.05.2018 00:00:00' UNION ALL
SELECT 12, 3.5, '09.05.2018 00:00:00' UNION ALL
SELECT 13, 1, '09.05.2018 00:00:00' UNION ALL
SELECT 14, 4, '04.05.2018 00:00:00' UNION ALL
SELECT 15, 1, '03.05.2018 00:00:00' UNION ALL
SELECT 16, 0, '02.05.2018 00:00:00' UNION ALL
SELECT 17, 3, '05.05.2018 00:00:00' UNION ALL
SELECT 18, 0.5, '04.05.2018 00:00:00' UNION ALL
SELECT 19, 2, '09.05.2018 00:00:00' UNION ALL
SELECT 20, 0, '09.05.2018 00:00:00'
--DATEADD(DAY, -1,Workdate)
;WITH Dates AS(
SELECT Workdate=#StartDate,WorkMonth=DATENAME(MONTH,#StartDate),WorkYear=YEAR(#StartDate), WorkWeek=datename(wk, DateAdd(DAY,-1,#StartDate) )
UNION ALL
SELECT CurrDate=DateAdd(DAY,1,Workdate),WorkMonth=DATENAME(MONTH,DateAdd(DAY,1,Workdate)),YEAR(DateAdd(DAY,1,Workdate)),datename(wk, Workdate) FROM Dates D WHERE Workdate<#EndDate ---AND (DATENAME(MONTH,D.Workdate))=(DATENAME(MONTH,D.Workdate))
)
SELECT
WorkMonth,
NumWeek=ROW_NUMBER()OVER(PARTITION BY WorkMonth+cast(WorkYear as varchar(20)) ORDER BY WorkdateStart),
NumDayWeek,
WorkYear,
WorkdateStart,
WorkdateEnd,
TotalHours=SUM(TotalHours)
FROM
(
SELECT
D.Workdate,
D.WorkMonth,
D.WorkYear,
D.WorkWeek,
WorkdateStart=MIN(D.Workdate) over(partition by cast(WorkWeek as varchar(20))+workmonth+cast(WorkYear as varchar(20))),
WorkdateEnd=MAX(D.Workdate) over(partition by cast(WorkWeek as varchar(20))+workmonth+cast(WorkYear as varchar(20))),
NumDayWeek=datediff(day,MIN(D.Workdate) over(partition by cast(D.WorkWeek as varchar(20))+workmonth+cast(WorkYear as varchar(20))),MAX(D.Workdate) over(partition by cast(D.WorkWeek as varchar(20))+workmonth+cast(WorkYear as varchar(20))))+1,
T.TotalHours,
T.DayCount
FROM
Dates D
LEFT JOIN
(
SELECT T.WorkDate, TotalHours=sum(T.Hours), DayCount=sum(case when T.Hours>0 then 1 else 0 end) FROM
#TempTable T
GROUP BY
T.WorkDate
)T ON
T.WorkDate = D.Workdate
)Sub
GROUP BY
WorkMonth,
WorkYear,
WorkdateStart,
NumDayWeek,
WorkdateEnd
ORDER BY
WorkdateStart
option (maxrecursion 0)
I'm trying to use a case select if the day is Monday (or d is equal to 2), and the 'else' would handle every other day of the week.
Select to_char (sysdate, 'D') d from dual,
/*if 2 or Monday then run this part*/
CASE
2
Then
SELECT DISTINCT RPAD(CLAIM.CLAIM_NUMBER, 20) ||
LPAD(CLAIM.CLAIM_ID, 15, '0') ||
TO_CHAR(CLAIM.INCIDENT_DATE, 'MMDDYYYY') ||
LPAD(' ',2) ||
'D'
FROM CLAIM,CLAIMANT,INSURER,POLICY
WHERE CLAIM.CLAIM_ID = CLAIMANT.CLAIM_ID
AND POLICY.INSURER_NUMBER = INSURER.INSURER_NUMBER
AND INSURER.INSURER_NUMBER NOT IN ('7','8')
AND CLAIMANT.EXAMINER1_CODE NOT IN ('MAYCONV')
AND CLAIM.INSURANCE_TYPE = 2
AND CLAIM.JURISDICTION_CODE <> 1
AND CLAIMANT.CLAIMANT_TYPE_CODE in (4,7)
/*if Monday then go 2 days back*/
AND CLAIMANT.EDIT_DATE > (SYSDATE - 2)
/*if not equal to 2 or any day but Monday then run this part*/
Else
SELECT DISTINCT RPAD(CLAIM.CLAIM_NUMBER, 20) ||
LPAD(CLAIM.CLAIM_ID, 15, '0') ||
TO_CHAR(CLAIM.INCIDENT_DATE, 'MMDDYYYY') ||
LPAD(' ',2) ||
'D'
FROM CLAIM,CLAIMANT,INSURER,POLICY
WHERE CLAIM.CLAIM_ID = CLAIMANT.CLAIM_ID
AND POLICY.INSURER_NUMBER = INSURER.INSURER_NUMBER
AND INSURER.INSURER_NUMBER NOT IN ('7','8')
AND CLAIMANT.EXAMINER1_CODE NOT IN ('MAYCONV')
AND CLAIM.INSURANCE_TYPE = 2
AND CLAIM.JURISDICTION_CODE <> 1
AND CLAIMANT.CLAIMANT_TYPE_CODE in (4,7)
/*if not Monday then go 1 day back*/
AND CLAIMANT.EDIT_DATE > (SYSDATE - 1)
/
I didn't follow your code closely, but it seems you just want the last condition in your where clause to depend on the day of the week.
If so, you need something like (WARNING: I didn't check anything else in the code to make sure it is correct or efficient!) - the only change is in the last line. You don't need the select... from dual and you only need to write the select statement once.
SELECT DISTINCT RPAD(CLAIM.CLAIM_NUMBER, 20) ||
LPAD(CLAIM.CLAIM_ID, 15, '0') ||
TO_CHAR(CLAIM.INCIDENT_DATE, 'MMDDYYYY') ||
LPAD(' ',2) ||
'D'
FROM CLAIM,CLAIMANT,INSURER,POLICY
WHERE CLAIM.CLAIM_ID = CLAIMANT.CLAIM_ID
AND POLICY.INSURER_NUMBER = INSURER.INSURER_NUMBER
............
AND CLAIMANT.EDIT_DATE > SYSDATE - case to_char(sysdate, 'd') when 2 then 2 else 1 end
This work fine:
SELECT INTERVAL '1:0:0' HOUR TO SECOND FROM dual;
I'm trying to select some value from table and use it as INTERVAL. How can I use subquery instead of '1:0:0'?
SELECT INTERVAL (SELECT some_varchar_value from some_table) HOUR TO SECOND FROM DUAL;
But INTERVAL expects expression within single quotes ' '
You can use the TO_DSINTERVAL() function:
SELECT TO_DSINTERVAL(some_varchar_value)
FROM some_table;
But your string value needs to be in the right format. If it as shown, with just hours, minutes and seconds, you need to prepend a dummy zero for the number of days:
with some_table(some_varchar_value) as (
select '1:0:0' from dual
)
SELECT TO_DSINTERVAL('0 ' || some_varchar_value)
FROM some_table;
TO_DSINTERV
-----------
0 1:0:0.0
If your hours value can be greater than 24 then you will need to split that into whole days and the remaining hours:
with some_table(some_varchar_value) as (
select '1:2:3' from dual
union all select '99:45:15' from dual
)
SELECT TO_DSINTERVAL(
floor(to_number(regexp_substr(some_varchar_value, '[^:]+', 1, 1)) / 24)
|| ' ' || mod(to_number(regexp_substr(some_varchar_value, '[^:]+', 1, 1)), 24)
|| ':' || regexp_substr(some_varchar_value, '[^:]+', 1, 2)
|| ':' || regexp_substr(some_varchar_value, '[^:]+', 1, 3)
)
FROM some_table;
TO_DSINTERV
-----------
0 1:2:3.0
4 3:45:15.0
The inner part of that is splitting the original string into separate hour, minute and second components; the hour is then separate into days with floor(hours / 24) and the remaining hours with mod(hours, 24). You can see that more clearly with:
with some_table(some_varchar_value) as (
select '99:59:30' from dual
)
SELECT regexp_substr(some_varchar_value, '[^:]+', 1, 1), regexp_substr(some_varchar_value, '[^:]+', 1, 2), regexp_substr(some_varchar_value, '[^:]+', 1, 3)
FROM some_table;
with some_table(some_varchar_value) as (
select '1:2:3' from dual
union all select '99:45:15' from dual
)
SELECT regexp_substr(some_varchar_value, '[^:]+', 1, 1) as raw_hh,
regexp_substr(some_varchar_value, '[^:]+', 1, 2) as raw_mi,
regexp_substr(some_varchar_value, '[^:]+', 1, 3) as raw_ss,
floor(to_number(regexp_substr(some_varchar_value, '[^:]+', 1, 1)) / 24) as new_dd,
mod(to_number(regexp_substr(some_varchar_value, '[^:]+', 1, 1)), 24) as new_hh
FROM some_table;
RAW_HH RAW_MI RAW_SS NEW_DD NEW_HH
-------- -------- -------- ---------- ----------
1 2 3 0 1
99 45 15 4 3