PLSQL How to split each row based on begin and end date - sql

Could you please assist me to split each row and create multiple rows for each date between begin_date and end_date.
ID CODE VIEW BEGIN_DATE END_DATE
-------------------------------------------
10400 null 2 17-FEB-20 17-FEB-20
10650 null 2 17-FEB-20 18-FEB-20
10900 null 2 19-FEB-20 21-FEB-20
10901 null 2 21-FEB-20 02-MAR-20
11650 2723 2 02-MAR-20 04-MAR-20
11650 1002 2 02-MAR-20 04-MAR-20
11650 1001 2 02-MAR-20 04-MAR-20
11650 1000 2 02-MAR-20 04-MAR-20
Currently I'm using below query but it doesn't seem to work
select
r.*
from rec r
connect by level <= end_date - begin_date + 1;
what i want is to some thing like this
ID CODE VIEW DATE
----------------------------------
11650 2723 2 02-MAR-20
11650 2723 2 03-MAR-20
11650 2723 2 04-MAR-20
.... continue

Here's one option:
Sample data:
SQL> with test (id, code, cview, begin_date, end_date) as
2 (select 10400, null, 2, date '2020-02-17', date '2020-02-17' from dual union all
3 select 10650, null, 2, date '2020-02-17', date '2020-02-18' from dual union all
4 select 11650, 2723, 2, date '2020-03-02', date '2020-03-04' from dual
5 )
Query begins here:
6 select id,
7 code,
8 cview,
9 begin_date + column_value - 1 as datum
10 from test cross join
11 table(cast(multiset(select level from dual
12 connect by level <= end_date - begin_date + 1
13 ) as sys.odcinumberlist))
14 order by id, datum;
ID CODE CVIEW DATUM
---------- ---------- ---------- ----------
10400 2 17.02.2020
10650 2 17.02.2020
10650 2 18.02.2020
11650 2723 2 02.03.2020
11650 2723 2 03.03.2020
11650 2723 2 04.03.2020
6 rows selected.
SQL>

Related

FORMATING SELECT

I have a select which shows me how much lasts a process, it looks like:
SELECT
info1,
info2,
ifno3...
(DATE,'DD.MM.YYYY')DAY,
to_char(DATE,'HH24:MI') hour_from,
to_char(DATE_UNTIL,''HH24:MI'')hour_until,
REGEXP_SUBSTR((date_until-date)DAY TO SECOND,'d{2}:\d{2}')lasts_time
.....
and if a process lasts for example from 09.06.2021 23:00 to 10.06.2021 07:00, then the output of query from above will be:
info1 info2 info3 DAY hour_from hour_until lasts_time
info1| info2 |info3 |09.06.2021 |23:00 | 07:00 | 08:00
and i want it to change in
if this process lasts over night, then the output should be something like this:
info1 info2 info3 DAY hour_from hour_until lasts_time
info1| info2 |info3 |09.06.2021 |23:00 | 00:00 | 01:00
info1| info2 |info3 |10.06.2021 |00:00 | 07:00 | 07:00
so it will count only until the day ends, and if the process is still on going in the new day the output will come in a new row,
I think I have here to deal with union all, but I'm not sure, if anyone has any idea would be great
Here's one option. Read comments within code.
SQL> with
2 test (id, info1, date_from, date_to) as
3 -- sample data
4 (select 1, 'info1', to_date('09.06.2021 23:00', 'dd.mm.yyyy hh24:mi'),
5 to_date('10.06.2021 07:00', 'dd.mm.yyyy hh24:mi')
6 from dual
7 ),
8 temp as
9 -- row generator, to generate all hours between DATE_FROM and DATE_TO
10 (select id, info1,
11 date_from + (level - 1) / 24 datum,
12 lead(date_from + (level - 1) / 24) over (order by date_from) next_datum
13 from test
14 connect by level <= (date_to - date_from) * 24 + 1
15 ),
16 temp2 as
17 -- find date boundaries (MINDAT and MAXDAT), as well as duration in between (LASTS_TIME)
18 (select id, info1, datum, next_datum,
19 min(datum) mindat,
20 max(case when to_char(next_datum, 'mi') = '00' then next_datum - 1/(24*60*60)
21 else datum
22 end
23 ) maxdat,
24 --
25 round((max(case when to_char(next_datum, 'mi') = '00' then next_datum - 1/(24*60*60)
26 else datum
27 end
28 ) -
29 min(datum)
30 ) * 24) lasts_time
31 from temp
32 group by id, info1, datum, next_datum
33 )
34 -- the final result
35 select id,
36 info1,
37 to_char(datum, 'dd.mm.yyyy') day,
38 min(to_char(datum, 'hh24:mi')) hour_from,
39 max(to_char(next_datum, 'hh24:mi')) hour_until,
40 sum(lasts_time) lasts_time
41 from temp2
42 group by id, info1,
43 to_char(datum, 'dd.mm.yyyy')
44 order by day;
The result is
ID INFO1 DAY HOUR_FROM HOUR_UNTIL LASTS_TIME
---------- ----- ---------- ---------- ---------- ----------
1 info1 09.06.2021 23:00 00:00 1
1 info1 10.06.2021 00:00 07:00 7
SQL>
For #astentx's comment: there's no endless loop in my case for values you suggested:
SQL> set timing on
SQL> with
2 test (id, info1, date_from, date_to) as
3 -- sample data
4 (select 1, 'info1', to_date('10.06.2021 07:00', 'dd.mm.yyyy hh24:mi'),
5 to_date('11.06.2021 07:00', 'dd.mm.yyyy hh24:mi')
6 from dual
7 ),
<snip>
ID INFO1 DAY HOUR_ HOUR_ LASTS_TIME
---------- ----- ---------- ----- ----- ----------
1 info1 10.06.2021 07:00 23:00 17
1 info1 11.06.2021 00:00 07:00 7
Elapsed: 00:00:00.04
SQL>
You can use recursive CTE to generate required amount of rows (which are equal to days distance between dates).
with a as (
select
'info1' as id
, to_date('2021-05-01 10:20:30') as dt_from
, to_date('2021-05-03 07:08:09') as dt_to
from dual
union all
select
'info2'
, to_date('2021-05-02 05:06:07')
, to_date('2021-05-02 10:20:30')
from dual
)
, b(id, dt_from, dt_to, dt) as (
select
id
, dt_from
, dt_to
, trunc(dt_from)
from a
union all
select
id
, dt_from
, dt_to
, dt + 1
from b
where dt < trunc(dt_to)
)
select
id
, dt
, numtodsinterval(
case
when trunc(dt_to) = dt
then dt_to
else dt + 1
end
-
greatest(dt_from, dt)
, 'DAY') as dur_interval
/*Or any rounding function you need*/
, trunc((case
when trunc(dt_to) = dt
then dt_to
else dt + 1
end
-
greatest(dt_from, dt))*24) as dur_hours
, case
when trunc(dt_from) = dt
then to_char(dt_from, 'hh24:mi')
else '00:00'
end as hour_from
, case
when trunc(dt_to) = dt
then to_char(dt_to, 'hh24:mi')
else '23:59'
end as hour_to
from b
order by
id
, dt
ID | DT | DUR_INTERVAL | DUR_HOURS | HOUR_FROM | HOUR_TO
:---- | :------------------ | :---------------------------- | --------: | :-------- | :------
info1 | 2021-05-01 00:00:00 | +000000000 13:39:30.000000000 | 13 | 10:20 | 23:59
info1 | 2021-05-02 00:00:00 | +000000001 00:00:00.000000000 | 24 | 00:00 | 23:59
info1 | 2021-05-03 00:00:00 | +000000000 07:08:09.000000000 | 7 | 00:00 | 07:08
info2 | 2021-05-02 00:00:00 | +000000000 05:14:23.000000000 | 5 | 05:06 | 10:20
db<>fiddle here

Oracle: Calculate the count() based on the past 6 month interval for each rows

I have the following data (the data is available from 2017 - Present)
SELECT * FROM TABLE1 WHERE DATE > TO_DATE('01/01/2019','MM/DD/YYYY')
Emp_ID Date Vehicle_ID Working_Hours
1005 01/01/2019 X500 7
1005 01/02/2019 X500 6
1005 01/03/2019 X700 7
1005 01/04/2019 X500 5
1005 01/05/2019 X700 7
1005 01/06/2019 X500 7
1006 01/01/2019 X500 7
1006 01/02/2019 X500 6
1006 01/03/2019 X700 7
1006 01/04/2019 X500 5
1006 01/05/2019 X700 7
1006 01/06/2019 X500 7
I need to calculate two columns.
LAST_6M_UNIQ_Vehicle_Count ==> Count of Unique Vehicle ID in the last(past) 6 months for that employee
LAST_6M_Vehicle_Count ==> Count of all vehicle ID for that employee in the Past 6 months
Note: Past 6 month from the date column
Expected output:
Emp_ID Date Vehicle_ID Working_Hours LAST_6M_UNIQ_Vehicle_Count LAST_6M_Vehicle_Count
1005 01/01/2019 X500 7 6 66
1005 01/02/2019 X500 6 7 62
1005 01/03/2019 X700 7 6 63
1005 01/04/2019 X500 5 7 67
1005 01/05/2019 X700 7 7 66
1005 01/06/2019 X500 7 7 67
. . . .
. . . .
. . . .
1005 03/20/2019 X600 6 12 75
1006 01/01/2019 X500 7 11 74
1006 01/02/2019 X500 6 10 66
1006 01/03/2019 X700 7 11 72
1006 01/04/2019 X500 5 13 67
1006 01/05/2019 X700 7 12 64
1006 01/06/2019 X500 7 12 63
For example, in the first row, the value for LAST_6M_UNIQ_Vehicle_Count is 6 because for the employee id 1005, the unique count of vehicle id between ((01/01/2019) - 6 month) and 01/01/2019 has 6 different vehicle id in them.
I tried Over and Partition by but the 6 month interval is missing
SELECT t.*, COUNT(DISTINCT t.VEHICLE_ID) OVER (PARTITION BY t.EMP_ID ORDER BY t.DATE)
AS LAST_6M_UNIQ_Vehicle_Count
FROM TABLE1 t
I am not able to calculate the values based on 6 month interval for each rows.
Your help is much appreciated.
Oracle doesn't like COUNT( DISTINCT ... ) OVER ( ... ) when used in a windowed analytic function with a range and will raise an ORA-30487: ORDER BY not allowed here exception (otherwise, that would be the solution). It will work without the DISTINCT keyword but not with it.
Instead, you can use a correlated sub-query:
SELECT t.*,
( SELECT COUNT( DISTINCT vehicle_id )
FROM table_name c
WHERE c.emp_id = t.emp_id
AND c."DATE" <= t."DATE"
AND ADD_MONTHS( t."DATE", -6 ) <= c."DATE"
) AS last_6m_uniq_vehicle_count,
COUNT(t.vehicle_id) OVER (
PARTITION BY t.emp_id
ORDER BY t."DATE"
RANGE BETWEEN INTERVAL '6' MONTH PRECEDING
AND CURRENT ROW
) AS last_6m_vehicle_count
FROM table_name t
Which for the sample data:
CREATE TABLE table_name ( vehicle_id, emp_id, "DATE" ) AS
SELECT 1, 1, DATE '2020-08-31' FROM DUAL UNION ALL
SELECT 2, 1, DATE '2020-07-31' FROM DUAL UNION ALL
SELECT 1, 1, DATE '2020-06-30' FROM DUAL UNION ALL
SELECT 2, 1, DATE '2020-05-31' FROM DUAL UNION ALL
SELECT 2, 1, DATE '2020-04-30' FROM DUAL UNION ALL
SELECT 2, 1, DATE '2020-03-31' FROM DUAL UNION ALL
SELECT 2, 1, DATE '2020-02-29' FROM DUAL UNION ALL
SELECT 2, 1, DATE '2020-01-31' FROM DUAL UNION ALL
SELECT 3, 1, DATE '2020-01-31' FROM DUAL;
Outputs:
VEHICLE_ID | EMP_ID | DATE | LAST_6M_UNIQ_VEHICLE_COUNT | LAST_6M_VEHICLE_COUNT
---------: | -----: | :-------- | -------------------------: | --------------------:
2 | 1 | 31-JAN-20 | 2 | 2
3 | 1 | 31-JAN-20 | 2 | 2
2 | 1 | 29-FEB-20 | 2 | 3
2 | 1 | 31-MAR-20 | 2 | 4
2 | 1 | 30-APR-20 | 2 | 5
2 | 1 | 31-MAY-20 | 2 | 6
1 | 1 | 30-JUN-20 | 3 | 7
2 | 1 | 31-JUL-20 | 3 | 8
1 | 1 | 31-AUG-20 | 2 | 7
db<>fiddle here
You can do this with window functions, and a range frame specification.
Computing the distinct count is a bit tricky: Oracle does not support it directly, but we can proceed in two steps. First perform a window count within employee/vehicle partitions, and then take in account only the first occurence of each vehicle in the employee partition.
So:
select vehicle_id, emp_id, "DATE",
sum(case when flag = 1 then 1 else 0 end) over(
partition by emp_id
order by "DATE"
range between interval '6' month preceding and current row
) as last_6m_uniq_vehicle_count,
count(*) over (
partition by emp_id
order by "DATE"
range between interval '6' month preceding and current row
) as last_6m_vehicle_count
from (
select t.*,
count(*) over (
partition by emp_id , vehicle_id
order by "DATE"
range between interval '6' month preceding and current row
) as flag
from table_name t
) t
order by "DATE", vehicle_id
As MTO points out, count(distinct) cannot be used as a window function to solve this.
For that reason, I would go for a lateral join:
select t.*, l.*
from t cross join lateral
(select count(*) as last_6m_vehicle_count, count(distinct t.vehicle_id) as last_6m_uniq_vehicle_count
from t t2
where t2.emp_id = t.emp_id and
t2.dte <= t.dte and
t2.dte > add_months(t.dte, -6)
) l;
Here is a db<>fiddle.

How to List Specification Data

I wrote a view. Example of view you can see under the code.
I am writing a script for the list data.
Example of view:
DONEM_ID, URUN_ID, TARIFF, TARIFE_PRI, START_DATE, END_DATE
xx10 1 12 123 01-02-2003 null
xx11 1 12 123 01-02-2003 01-11-2003
xx12 1 12 124 01-11-2003 null
I wanna list
URUN_ID, TARIFF, TARIFE_PRI, START_DATE, END_DATE
1 12 123 01-02-2003 01-11-2003
1 12 124 01-11-2003 null
How can i list this?
Aggregation usually helps (code you might need begins at line #8):
SQL> alter session set nls_date_format = 'mm-dd-yyyy';
Session altered.
SQL> with
2 -- sample data; you have that, don't type it
3 test (donem_id, urun_id, tariff, tarife_pri, start_date, end_date) as
4 (select 'xx10', 1, 12, 123, date '2003-01-02', null from dual union all
5 select 'xx11', 1, 12, 123, date '2003-01-02', date '2003-01-11' from dual union all
6 select 'xx12', 1, 12, 124, date '2003-01-11', null from dual
7 )
8 select urun_id, tariff, tarife_pri, min(start_date) start_date, max(end_date) end_date
9 from test
10 group by urun_id, tariff, tarife_pri
11 order by urun_id, tariff, tarife_pri;
URUN_ID TARIFF TARIFE_PRI START_DATE END_DATE
---------- ---------- ---------- ---------- ----------
1 12 123 01-02-2003 01-11-2003
1 12 124 01-11-2003
SQL>

Getting a date from a 4 digit strange date format in Oracle SQL

I receive a string from decoding codebar-128, once I parse all data in the code readed I get a date in a strange 4 digits format: 'YDDD'
The 'Y' digit represents the last digit of the Year (0-9). The 'DDD' digits represent the Day of year (1-366).
The issue is the ambiguous value of the Year. The rule to solve that issue must be the follow:
The Year computed for 'Y' digit must be the nearest year to Sysdate year.
Never the difference from Sysdate year and computed year for the 'Y' digit will be greater than 4.
My code:
SELECT SYSDATE, TO_DATE('0213', 'YDDD'), TO_DATE('1212', 'YDDD'),
TO_DATE('2212', 'YDDD'), TO_DATE('3212', 'YDDD'), TO_DATE('4213', 'YDDD'),
TO_DATE('6212', 'YDDD'), TO_DATE('7212', 'YDDD'), TO_DATE('8213', 'YDDD'),
TO_DATE('9212', 'YDDD')
FROM dual;
This is that I need to get:
+-----------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+
| SYSDATE | TO_DATE('20213','YYDDD') | TO_DATE('21212','YYDDD') | TO_DATE('22212','YYDDD') | TO_DATE('23212','YYDDD') | TO_DATE('24213','YYDDD') | TO_DATE('16213','YYDDD') | TO_DATE('17212','YYDDD') | TO_DATE('18212','YYDDD') | TO_DATE('19212','YYDDD') |
+-----------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+
| 26-JUN-20 | 31-JUL-20 | 31-JUL-21 | 31-JUL-22 | 31-JUL-23 | 31-JUL-24 | 31-JUL-16 | 31-JUL-17 | 31-JUL-18 | 31-JUL-19 |
+-----------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+
As you can see, if I had the penultimate digit of the year there would be no issue.
This is that I'm really getting:
+-----------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+
| SYSDATE | TO_DATE('0213','YDDD') | TO_DATE('1212','YDDD') | TO_DATE('2212','YDDD') | TO_DATE('3212','YDDD') | TO_DATE('4213','YDDD') | TO_DATE('6212','YDDD') | TO_DATE('7212','YDDD') | TO_DATE('8213','YDDD') | TO_DATE('9212','YDDD') |
+-----------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+
| 26-JUN-20 | 31-JUL-20 | 31-JUL-21 | 31-JUL-22 | 31-JUL-23 | 31-JUL-24 | 31-JUL-26 | 31-JUL-27 | 31-JUL-28 | 31-JUL-29 |
+-----------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+
You can compare the single-digit value with the last digit of the current year, and if the difference is more than 4, adjust but 10 years. But it needs to go both ways; once 'today' is in 2026, you'll be adding 10 years instead.
select column_value as val,
to_date(column_value, 'YDDD') as dt1,
to_number(substr(column_value, 1, 1)) as y,
mod(extract(year from sysdate), 10) as yy,
case
when to_number(substr(column_value, 1, 1)) - mod(extract(year from sysdate), 10) > 4 then -10
when mod(extract(year from sysdate), 10) - to_number(substr(column_value, 1, 1)) > 4 then 10
else 0
end as adj,
to_date(column_value, 'YDDD')
+ case
when to_number(substr(column_value, 1, 1)) - mod(extract(year from sysdate), 10) > 4 then -10
when mod(extract(year from sysdate), 10) - to_number(substr(column_value, 1, 1)) > 4 then 10
else 0
end * interval '1' year as dt2,
add_months(to_date(column_value, 'YDDD'),
12 * case
when to_number(substr(column_value, 1, 1)) - mod(extract(year from sysdate), 10) > 4 then -10
when mod(extract(year from sysdate), 10) - to_number(substr(column_value, 1, 1)) > 4 then 10
else 0
end) as dt2
from table(sys.odcivarchar2list('0213', '1212', '2212', '3212', '4213',
'5212', '6212', '7212', '8213', '9212'));
which gets
VAL DT1 Y YY ADJ DT2 DT2
---- ---------- ---------- ---------- ---------- ---------- ----------
0213 2020-07-31 0 0 0 2020-07-31 2020-07-31
1212 2021-07-31 1 0 0 2021-07-31 2021-07-31
2212 2022-07-31 2 0 0 2022-07-31 2022-07-31
3212 2023-07-31 3 0 0 2023-07-31 2023-07-31
4213 2024-07-31 4 0 0 2024-07-31 2024-07-31
5212 2025-07-31 5 0 -10 2015-07-31 2015-07-31
6212 2026-07-31 6 0 -10 2016-07-31 2016-07-31
7212 2027-07-31 7 0 -10 2017-07-31 2017-07-31
8213 2028-07-31 8 0 -10 2018-07-31 2018-07-31
9212 2029-07-31 9 0 -10 2019-07-31 2019-07-31
I haven't verified the future-year behaviour so you probably need to test and adjust that as necessary.
Split it up in multiple with clauses so it is easier to understand, you can join it into a single query if you want.
WITH sampledata (dt) AS
(
SELECT '0213' FROM DUAL UNION
SELECT '1212' FROM DUAL UNION
SELECT '2212' FROM DUAL UNION
SELECT '3212' FROM DUAL UNION
SELECT '4213' FROM DUAL UNION
SELECT '5213' FROM DUAL UNION
SELECT '6212' FROM DUAL UNION
SELECT '7212' FROM DUAL UNION
SELECT '8213' FROM DUAL UNION
SELECT '9212' FROM DUAL
), parsed_sampledata (yr, ddd) AS
(
SELECT substr(d.dt,1, 1) + TO_CHAR(SYSDATE,'YY') as yr, substr(d.dt,2,3) as ddd
FROM sampledata d
)
SELECT TO_DATE(ddd||yr - (CASE WHEN yr - TO_CHAR(SYSDATE,'YY') < 5 THEN 0 ELSE 10 END),'DDDYY')
FROM parsed_sampledata d;
31-JUL-2020
31-JUL-2021
31-JUL-2022
31-JUL-2023
31-JUL-2024
01-AUG-2015
30-JUL-2016
31-JUL-2017
01-AUG-2018
31-JUL-2019
This should give you some ideas:
WITH DATES_LIST AS
(
SELECT '0213' AS D FROM DUAL UNION
SELECT '1212' AS D FROM DUAL UNION
SELECT '2212' AS D FROM DUAL UNION
SELECT '3212' AS D FROM DUAL UNION
SELECT '4213' AS D FROM DUAL UNION
SELECT '5213' AS D FROM DUAL UNION
SELECT '6213' AS D FROM DUAL UNION
SELECT '7212' AS D FROM DUAL UNION
SELECT '8212' AS D FROM DUAL UNION
SELECT '9212' AS D FROM DUAL
)
SELECT TO_DATE(REGEXP_REPLACE(D,'^\d{1}',
CASE WHEN BOTT_R <= UPP_R THEN BOT ELSE UPP END),'YYDDD') AS YEAR
FROM (
select D,(TO_CHAR(SYSDATE,'RR') - 10) + regexp_substr(D, '^\d{1}') BOT,
ABS((TO_CHAR(SYSDATE,'RR') - 10) + regexp_substr(D, '^\d{1}')-TO_CHAR(SYSDATE,'RR')) BOTT_R,
TO_CHAR(SYSDATE,'RR') + regexp_substr(D, '^\d{1}') UPP,
(TO_CHAR(SYSDATE,'RR') + regexp_substr(D, '^\d{1}')) - TO_CHAR(SYSDATE,'RR') UPP_R
from DATES_LIST);
If you need to convert to many variables(many) my advise is to create a DETERMINISTIC function.
Regards.

Oracle Sysdate > NULL

I am looking into a scenario some similar to future dated stuff.
I have a table something similar to this
ProductID ProductStatus EffectiveFromDate EffectiveToDate CancelledIndicator
----------- ------------- ----------------- --------------- ------------------
345 A 7/7/2016 (null) 1
345 S 7/7/2016 11/7/2016 (null)
345 A 12/7/2016 (null) (null)
I need to fetch the current dated product
if I find a cancelled indicator it means it is no more active
if their are two rows one with future dated status.
Based on the table above I get a latest record if I check for the efd < sysdate and etd is null. but to get the current active status which is the case which i need to implement.
I need to check if the sysdate is b/w the efd and etd of the older record if not I need to take the latest record which will be the current status.
I have query which does that
but the thing is what happens when I check
sysdate between efd and etd where etd can be null most of the time.
Some samples of how you can handle it:
setup:
SQL> create table testNull (id, startDate, endDate) as
2 (
3 select 1, null, sysdate + 1 from dual union all
4 select 2, sysdate -1, sysdate + 1 from dual union all
5 select 3, sysdate -1, null from dual union all
6 select 4, sysdate -3, sysdate - 1 from dual
7 );
Table created.
without handling NULL:
SQL> select *
2 from testNull
3 where sysdate between startDate and endDate ;
ID STARTDATE ENDDATE
---------- --------- ---------
2 11-LUG-16 13-LUG-16
with COALESCE:
SQL> select *
2 from testNull
3 where sysdate between startDate and coalesce(endDate, sysdate);
ID STARTDATE ENDDATE
---------- --------- ---------
2 11-LUG-16 13-LUG-16
3 11-LUG-16
with some boolean logic:
SQL> select *
2 from testNull
3 where sysdate >= startDate
4 and ( endDate is null or sysdate <= endDate);
ID STARTDATE ENDDATE
---------- --------- ---------
2 11-LUG-16 13-LUG-16
3 11-LUG-16