Convert Specific Field of Rows into seperated columns in oracle - oracle

i've face with a challenging case. I really appreciate if someone helps ...
imagine we have following set of records:
ID
Date
20220614175302951087
6/15/2022 13:27
20220614175302951087
6/15/2022 14:06
20220614175302951088
6/15/2022 13:27
20220614175302951088
6/15/2022 14:07
20220614175302951088
6/15/2022 14:42
20220614175302951089
6/15/2022 13:28
20220614175302951089
6/15/2022 14:08
20220614175302951089
6/15/2022 14:39
20220614175302951090
6/15/2022 13:38
20220614175802951200
6/15/2022 9:58
20220614175802951200
6/15/2022 10:28
i need the result to be shown as
ID
Date1
Date2
Date3
20220614175302951087
6/15/2022 13:27
6/15/2022 14:06
20220614175302951088
6/15/2022 13:27
6/15/2022 14:07
6/15/2022 14:42
20220614175302951089
6/15/2022 13:28
6/15/2022 14:08
6/15/2022 14:39
20220614175802951200
6/15/2022 9:58
6/15/2022 10:28
thank you guys

If the no of dates are maximum 3, You can use the conditional aggregation method along with ROW_NUMBER, to achieve your result -
SELECT ID,
MAX(CASE WHEN RN = 1 THEN DATE ELSE NULL END) Date1,
MAX(CASE WHEN RN = 2 THEN DATE ELSE NULL END) Date2,
MAX(CASE WHEN RN = 3 THEN DATE ELSE NULL END) Date3
FROM (SELECT ID, DATE,
ROW_NUMBER() OVER(PARTITION BY ID ORDER BY DATE)
FROM YOUR_TABLE)
GROUP BY ID;

Related

ORACLE GROUP BY with Date does not group correctly

I try to group a given table by date to get a min and max date of member IDs. The result should display a time range from when to when a member was part of an OE.
my given table (excerpt):
ID DATE OE
11 2021-03-06 00:00:00 2926
11 2021-03-07 00:00:00 3879
11 2021-03-08 00:00:00 3879
11 2021-03-09 00:00:00 3879
11 2021-03-10 00:00:00 2926
11 2021-03-11 00:00:00 2926
11 2021-03-12 00:00:00 2926
11 2021-03-13 00:00:00 2926
11 2021-03-14 00:00:00 2926
11 2021-03-15 00:00:00 2926
11 2021-03-16 00:00:00 1344
11 2021-03-17 00:00:00 1344
11 2021-03-18 00:00:00 1344
11 2021-03-19 00:00:00 1344
11 2021-03-20 00:00:00 1344
11 2021-03-21 00:00:00 1344
11 2021-03-22 00:00:00 2926
11 2021-03-23 00:00:00 2926
11 2021-03-24 00:00:00 2926
11 2021-03-25 00:00:00 2926
11 2021-03-26 00:00:00 2926
11 2021-03-27 00:00:00 2926
11 2021-03-28 00:00:00 2926
11 2021-03-29 00:00:00 2926
11 2021-03-30 00:00:00 2926
11 2021-03-31 00:00:00 2926
11 2021-04-01 00:00:00 1549
11 2021-04-02 00:00:00 1549
11 2021-04-03 00:00:00 1549
11 2021-04-04 00:00:00 2926
My Select:
select id, min(date) as mind, max(date) as maxd,OE
from <table>
group by id,oe
order by mind desc;
The output should be something like this:
ID | MIND | MAXD | OE
11 2021-04-04 00:00:00 2021-04-04 00:00:00 2926
11 2021-04-01 00:00:00 2021-04-03 00:00:00 1549
11 2021-03-22 00:00:00 2021-03-31 00:00:00 2926
11 2021-03-16 00:00:00 2021-03-21 00:00:00 1344
11 2021-03-10 00:00:00 2021-03-15 00:00:00 2926
11 2021-03-07 00:00:00 2021-03-09 00:00:00 3879
11 2021-03-06 00:00:00 2021-03-06 00:00:00 2926
But it is like this:
ID | MIND | MAXD | OE
11 2021-04-01 00:00:00 2021-04-03 00:00:00 1549
11 2021-03-16 00:00:00 2021-03-21 00:00:00 1344
11 2021-03-07 00:00:00 2021-03-09 00:00:00 3879
11 2021-03-06 00:00:00 2021-04-04 00:00:00 2926
The result should display a time range from when to when a member was part of an OE. Even if I add some other IDs (which I obviously have) it does not show the timeline of OE changes in correct order.
Any help highly appreciated!
TIA,
Michael
You can use a trick called tabibitosan to do this kind of grouping:
SELECT id,
MIN(dt) AS mind,
MAX(dt) AS maxd,
oe
FROM (SELECT id,
dt,
oe,
row_number() OVER (PARTITION BY ID ORDER BY dt) - row_number() OVER (PARTITION BY ID, oe ORDER BY dt) grp
FROM your_table)
GROUP BY id,
oe,
grp
ORDER BY mind DESC;
See this dbfiddle for results
This works by assigning row numbers across the whole set of data (in this case, it's across each id), and then finding the row numbers across the subsets of data (i.e. across each id and oe), and then subtracting one from the other to form a number you can group by. Consecutive rows get the same group number, but every time there's a non-consecutive row, the group number will change.
From Oracle 12, you can use MATCH_RECOGNIZE for row-by-row pattern matching:
SELECT *
FROM table_name
MATCH_RECOGNIZE(
PARTITION BY id
ORDER BY "DATE"
MEASURES
FIRST(oe) AS oe,
FIRST("DATE") AS mind,
LAST("DATE") AS maxd
PATTERN (same_oe+)
DEFINE
same_oe AS oe = FIRST(oe)
)
Which, for the sample data:
CREATE TABLE table_name (ID, "DATE", OE) AS
SELECT 11, DATE '2021-03-06', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-07', 3879 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-08', 3879 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-09', 3879 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-10', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-11', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-12', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-13', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-14', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-15', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-16', 1344 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-17', 1344 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-18', 1344 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-19', 1344 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-20', 1344 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-21', 1344 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-22', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-23', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-24', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-25', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-26', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-27', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-28', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-29', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-30', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-03-31', 2926 FROM DUAL UNION ALL
SELECT 11, DATE '2021-04-01', 1549 FROM DUAL UNION ALL
SELECT 11, DATE '2021-04-02', 1549 FROM DUAL UNION ALL
SELECT 11, DATE '2021-04-03', 1549 FROM DUAL UNION ALL
SELECT 11, DATE '2021-04-04', 2926 FROM DUAL;
Outputs:
ID
OE
MIND
MAXD
11
2926
2021-03-06 00:00:00
2021-03-06 00:00:00
11
3879
2021-03-07 00:00:00
2021-03-09 00:00:00
11
2926
2021-03-10 00:00:00
2021-03-15 00:00:00
11
1344
2021-03-16 00:00:00
2021-03-21 00:00:00
11
2926
2021-03-22 00:00:00
2021-03-31 00:00:00
11
1549
2021-04-01 00:00:00
2021-04-03 00:00:00
11
2926
2021-04-04 00:00:00
2021-04-04 00:00:00
fiddle

Merge Columns into One Column Oracle PL/SQL

I have the following script and tables where upon running the script produces the output for LOG_ID, YEAR, WA.SUB_DIVISION, AI.SUB_DIVISION, EA.SUB_DIVISION, FI.SUB_DIVISION
Is it possible to Merge four columns into one column
WA.SUB_DIVISION, AI.SUB_DIVISION, EA.SUB_DIVISION, FI.SUB_DIVISION into SUB_DIVISION a single column
Not sure how to proceed.
I have created a sample sql fiddle
https://dbfiddle.uk/?rdbms=oracle_18&fiddle=3c4abb924462dcf5e5f8b0f91019b6b6
select distinct L.LOG_ID,
FC.LOG_YR as YEAR,
WA.SUB_DIVISION,
AI.SUB_DIVISION AS SUB_DIV,
EA.SUB_DIVISION AS SUB_DIV3,
FI.SUB_DIVISION AS SUB_DIV4
FROM FINAL_CALENDAR FC
JOIN LOG L
ON TO_DATE ( TO_CHAR (L.LOG_DATE, 'MM/DD/YYYY'), 'MM/DD/YYYY') = FC.CAL_DATE
LEFT OUTER JOIN LOG_WATER WA
ON WA.LOG_ID = L.LOG_ID
LEFT OUTER JOIN LOG_AIR AI
ON AI.LOG_ID = L.LOG_ID
LEFT OUTER JOIN LOG_EARTH EA
ON EA.LOG_ID = L.LOG_ID
LEFT OUTER JOIN LOG_FIRE FI
ON FI.LOG_ID = L.LOG_ID
Actual Output / ISSUE / Existing Output
LOG_ID YEAR SUB_DIVISION SUB_DIV SUB_DIV3 SUB_DIV4
990741 2020 NULL NULL NULL NULL
990742 2020 NULL NULL NULL NULL
991122 2020 NULL NULL NULL NULL
991123 2020 NULL NULL NULL NULL
994461 2020 NULL 4 NULL NULL
994468 2020 NULL 2 NULL NULL
994466 2020 NULL 2 NULL NULL
994480 2020 8 NULL NULL NULL
994479 2020 8 NULL NULL NULL
994476 2020 6 NULL NULL NULL
994478 2020 6 NULL NULL NULL
994440 2020 NULL NULL NULL NULL
994432 2020 NULL NULL NULL NULL
994450 2020 NULL NULL NULL NULL
994154 2020 NULL NULL NULL NULL
Required / Desired Output
LOG_ID YEAR SUB_DIVISION DISPLAY_NAME
990741 2020 NULL NULL
990742 2020 NULL NULL
991122 2020 NULL NULL
991123 2020 NULL NULL
994461 2020 4 Triangle
994468 2020 2 Circle
994466 2020 2 Circle
994480 2020 8 Rhombus
994479 2020 8 Rhombus
994476 2020 6 Dot
994478 2020 6 Dot
994440 2020 NULL NULL
994432 2020 NULL NULL
994450 2020 NULL NULL
994154 2020 NULL NULL
Table LOG;
LOG_ID, LOG_DATE,
990741, to_date('21-JAN-20','DD-MON-RR')
990742 21-JAN-20
991122 24-JAN-20
991123 25-JAN-20
994461 25-JAN-20
994468 25-JAN-20
994466 25-JAN-20
994480 25-JAN-20
994479 25-JAN-20
994476 25-JAN-20
994478 25-JAN-20
994440 25-JAN-20
994432 25-JAN-20
994450 25-JAN-20
994154 25-JAN-20
TABLE FINAL_CALENDAR;
CAL_DATE CAL_MONTH LOG_YR
21-JAN-20 1 2020
21-JAN-20 1 2020
24-JAN-20 1 2020
25-JAN-20 1 2020
25-JAN-20 1 2020
25-JAN-20 1 2020
25-JAN-20 1 2020
25-JAN-20 1 2020
25-JAN-20 1 2020
25-JAN-20 1 2020
25-JAN-20 1 2020
25-JAN-20 1 2020
25-JAN-20 1 2020
25-JAN-20 1 2020
25-JAN-20 1 2020
TABLE LOG_AIR;
ID LOG_ID SUB_DIVISION
134 994468 2
132 994461 4
133 994466 2
TABLE LOG_WATER;
ID LOG_ID SUB_DIVISION
9345 994480 8
9344 994479 8
9342 994476 6
9343 994478 6
TABLE LOG_EARTH;
ID LOG_ID SUB_DIVISION
0118 994440 null
0117 994432 null
TABLE LOG_FIRE;
ID LOG_ID SUB_DIVISION
706 994450 null
705 994154 null
TABLE Z_SUB_DIVISION_TYPE;
SUB_DIVISION DISPLAY_NAME
1 Parallelogram
2 Circle
3 Square
4 Triangle
5 Tangent
6 Dot
7 Line
8 Rhombus
9 Trapezium
If that's the final result you can merge last four columns assuming one column has value and rest are null(sub division columns)
SELECT log_id,
year,
CASE
WHEN sub_division IS NULL AND SUB_DIV3 IS NULL AND SUB_DIV4 IS NULL THEN sub_div
WHEN sub_division IS NULL AND SUB_DIV IS NULL AND SUB_DIV4 IS NULL THEN sub_div3
WHEN sub_division IS NULL AND SUB_DIV IS NULL AND SUB_DIV3 IS NULL THEN sub_div4
ELSE
sub_division
END as sub_division,
display_name
FROM (SELECT DISTINCT L.log_id,
FC.log_yr AS YEAR,
WA.sub_division,
AI.sub_division AS SUB_DIV,
EA.sub_division
AS SUB_DIV3,
FI.sub_division AS SUB_DIV4,
(SELECT display_name
FROM z_sub_division_type a
WHERE a.sub_division = WA.sub_division
OR a.sub_division = AI.sub_division
OR a.sub_division = EA.sub_division
OR a.sub_division = FI.sub_division
) AS DISPLAY_NAME
FROM final_calendar FC
join log L
ON To_date (To_char (L.log_date, 'MM/DD/YYYY'), 'MM/DD/YYYY') =
FC.cal_date
left outer join log_water WA
ON WA.log_id = L.log_id
left outer join log_air AI
ON AI.log_id = L.log_id
left outer join log_earth EA
ON EA.log_id = L.log_id
left outer join log_fire FI
ON FI.log_id = L.log_id)
Edit 1:-This sql works assuming atleast one column has value and rest are null
Edit 2:- You can replace case clause with coalesce
coalesce(sub_division,sub_div,sub_div3,sub_div4) as sub_division

Oracle query for getting particular Date

I have an oracle query which returns next mont first sunday.
now i have a condition here to check if the date which has passed is first sunday of current month, then we need second sunday in current month.
Else, next month first sunday.
My query:
DEF startDate = to_date('somedate', 'dd/mm/yyyy');
Select next_day(last_day(&startDate),'Sunday' ) from dual ;
Expected output:
if we input 1st july 2018, it has to return 8th july 2018(second sunday) else, any other day apart from first sunday like, (2nd july 2018), it has to return 5th Aug 2018.
Input Expected Output
01-Jul-18 08-Jul-18,
02-Jul-18 05-Aug-18,
05-Aug-18 12-Aug-18,
19-Aug-18 02-Sep-18.
Based on the description in your question and comments you want something like:
case when start_date = next_day(trunc(start_date, 'MM') - 1, 'Sunday') -- date is on first sunday
then next_day(start_date, 'Sunday') -- next Sunday, which is second in month
else next_day(last_day(start_date), 'Sunday') -- first Sunday of next month
end
With some sample dates in a CTE, including some discussed but also others:
with cte (start_date) as (
select date '2018-05-30' from dual
union all select date '2018-06-01' from dual
union all select date '2018-06-02' from dual
union all select date '2018-06-03' from dual
union all select date '2018-06-04' from dual
union all select date '2018-06-30' from dual
union all select date '2018-07-01' from dual
union all select date '2018-07-02' from dual
union all select date '2018-07-03' from dual
union all select date '2018-07-04' from dual
union all select date '2018-07-05' from dual
union all select date '2018-07-06' from dual
union all select date '2018-07-07' from dual
union all select date '2018-07-08' from dual
union all select date '2018-07-31' from dual
union all select date '2018-08-01' from dual
union all select date '2018-08-02' from dual
union all select date '2018-08-03' from dual
union all select date '2018-08-04' from dual
union all select date '2018-08-05' from dual
union all select date '2018-08-06' from dual
)
select start_date,
to_char(start_date, 'Dy') as day,
case when to_char(start_date, 'Dy') = 'Sun'
then 'Yes' else 'No' end as is_sunday,
case when start_date = next_day(trunc(start_date, 'MM') - 1, 'Sunday')
then 'Yes' else 'No' end as is_first_sunday,
next_day(trunc(start_date, 'MM') - 1, 'Sunday') as first_sun_this_month,
next_day(trunc(start_date, 'MM') + 6, 'Sunday') as second_sun_this_month,
next_day(last_day(start_date), 'Sunday') as first_sun_next_month,
case when start_date = next_day(trunc(start_date, 'MM') - 1, 'Sunday') -- date is on first sunday
then next_day(start_date, 'Sunday') -- next Sunday, which is second in month
else next_day(last_day(start_date), 'Sunday') -- first Sunday of next month
end as result
from cte;
gets
START_DATE DAY IS_ IS_ FIRST_SUN_ SECOND_SUN FIRST_SUN_ RESULT
---------- ------------ --- --- ---------- ---------- ---------- ----------
2018-05-30 Wed No No 2018-05-06 2018-05-13 2018-06-03 2018-06-03
2018-06-01 Fri No No 2018-06-03 2018-06-10 2018-07-01 2018-07-01
2018-06-02 Sat No No 2018-06-03 2018-06-10 2018-07-01 2018-07-01
2018-06-03 Sun Yes Yes 2018-06-03 2018-06-10 2018-07-01 2018-06-10
2018-06-04 Mon No No 2018-06-03 2018-06-10 2018-07-01 2018-07-01
2018-06-30 Sat No No 2018-06-03 2018-06-10 2018-07-01 2018-07-01
2018-07-01 Sun Yes Yes 2018-07-01 2018-07-08 2018-08-05 2018-07-08
2018-07-02 Mon No No 2018-07-01 2018-07-08 2018-08-05 2018-08-05
2018-07-03 Tue No No 2018-07-01 2018-07-08 2018-08-05 2018-08-05
2018-07-04 Wed No No 2018-07-01 2018-07-08 2018-08-05 2018-08-05
2018-07-05 Thu No No 2018-07-01 2018-07-08 2018-08-05 2018-08-05
2018-07-06 Fri No No 2018-07-01 2018-07-08 2018-08-05 2018-08-05
2018-07-07 Sat No No 2018-07-01 2018-07-08 2018-08-05 2018-08-05
2018-07-08 Sun Yes No 2018-07-01 2018-07-08 2018-08-05 2018-08-05
2018-07-31 Tue No No 2018-07-01 2018-07-08 2018-08-05 2018-08-05
2018-08-01 Wed No No 2018-08-05 2018-08-12 2018-09-02 2018-09-02
2018-08-02 Thu No No 2018-08-05 2018-08-12 2018-09-02 2018-09-02
2018-08-03 Fri No No 2018-08-05 2018-08-12 2018-09-02 2018-09-02
2018-08-04 Sat No No 2018-08-05 2018-08-12 2018-09-02 2018-09-02
2018-08-05 Sun Yes Yes 2018-08-05 2018-08-12 2018-09-02 2018-08-12
2018-08-06 Mon No No 2018-08-05 2018-08-12 2018-09-02 2018-09-02
The result column is the one you're interested in, the others just try to show the working a bit.
It looks odd to have the result dates out of sequence compared to the input dates - 2018-06-03 going to 2018--06-10 then those both before and after it go to 2018-07-01. But that seems to be what you want.

Rank () function over multiple columns in oracle

I need to rank a table with two columns transID & travel_date
here is my data
transID travel_date
2341 2018-04-04 10:00:00
2341 2018-04-04 11:30:00
2891 2018-04-04 12:30:00
2891 2018-04-04 18:30:00
2341 2018-04-05 11:30:00
2891 2018-04-05 22:30:00
this is the query which i have tried
select transID,travel_date,rn,
dense_rank () over (partition by transID order by EarliestDate,transID) as rn2
from
(SELECT transID,travel_date,
ROW_NUMBER() OVER (PARTITION BY transID ORDER BY travel_date) AS rn,
max(travel_date) OVER (partition by travel_date) as EarliestDate
FROM travel_log_info
) t
order by transID;
Current Output from the above query
transID travel_date rn2
2341 2018-04-04 10:00:00 1
2341 2018-04-04 11:30:00 2
2341 2018-04-05 11:30:00 3
2891 2018-04-04 12:30:00 1
2891 2018-04-04 18:30:00 2
2891 2018-04-05 22:30:00 3
Expected Output
transID travel_date rn2
2341 2018-04-04 10:00:00 1
2341 2018-04-04 11:30:00 2
2341 2018-04-05 11:30:00 1
2891 2018-04-04 12:30:00 1
2891 2018-04-04 18:30:00 2
2891 2018-04-05 22:30:00 1
with this output, I can get the desired output by where condition rn2 = 1 to get the output based on travel date and transId.
I am not getting the desired output as shown above. Kindly provide suggestions to achieve the correct output.
Thanks for your time
The main problem with what you have now is:
max(travel_date) OVER (partition by travel_date)
which includes the time part of each date in the partition - so you're really getting the max of every individual date/time, which is that date/time. You seem to want maximum date/time within each day, so you could partition by each day by using trunc() in the partition-by clause:
max(travel_date) OVER (partition by trunc(travel_date))
Just that change gives you:
TRANSID TRAVEL_DATE RN RN2
---------- ------------------- ---------- ----------
2341 2018-04-04 10:00:00 1 1
2341 2018-04-04 11:30:00 2 1
2341 2018-04-05 11:30:00 3 2
2891 2018-04-04 12:30:00 1 1
2891 2018-04-04 18:30:00 2 1
2891 2018-04-05 22:30:00 3 2
The partitioning in the outer query is also wrong though, you need to partition by that 'earliest' date (actually latest, but doesn't matter for this):
select transID,travel_date,rn,
dense_rank () over (partition by transID,EarliestDate order by travel_date) as rn2
from
(SELECT transID,travel_date,
ROW_NUMBER() OVER (PARTITION BY transID ORDER BY travel_date) AS rn,
max(travel_date) OVER (partition by trunc(travel_date)) as EarliestDate
FROM travel_log_info
) t
order by transID;
TRANSID TRAVEL_DATE RN RN2
---------- ------------------- ---------- ----------
2341 2018-04-04 10:00:00 1 1
2341 2018-04-04 11:30:00 2 2
2341 2018-04-05 11:30:00 3 1
2891 2018-04-04 12:30:00 1 1
2891 2018-04-04 18:30:00 2 2
2891 2018-04-05 22:30:00 3 1
But you don't really need that max, or the outer query you currently have; if you include that truncated day in the row_number() partition (which you currently aren't really using) you get:
SELECT transID,travel_date,
ROW_NUMBER() OVER (PARTITION BY transID, trunc(travel_date) ORDER BY travel_date) AS rn
FROM travel_log_info;
TRANSID TRAVEL_DATE RN
---------- ------------------- ----------
2341 2018-04-04 10:00:00 1
2341 2018-04-04 11:30:00 2
2341 2018-04-05 11:30:00 1
2891 2018-04-04 12:30:00 1
2891 2018-04-04 18:30:00 2
2891 2018-04-05 22:30:00 1
and you can then wrap that in an outer query to filter on rn:
SELECT transID,travel_date
FROM (
SELECT transID,travel_date,
ROW_NUMBER() OVER (PARTITION BY transID, trunc(travel_date) ORDER BY travel_date) AS rn
FROM travel_log_info
)
WHERE rn = 1
ORDER BY transID,travel_date;
TRANSID TRAVEL_DATE
---------- -------------------
2341 2018-04-04 10:00:00
2341 2018-04-05 11:30:00
2891 2018-04-04 12:30:00
2891 2018-04-05 22:30:00
You could also do this without a subquery; this gets the same result using first:
SELECT transID,
min(travel_date) keep (dense_rank first order by travel_date) as travel_date
FROM travel_log_info
GROUP BY transID, trunc(travel_date)
ORDER BY transID, travel_date;

Oracle TO_DATE format, symbol for "any character"

In this answer the following query is suggested:
SELECT TO_DATE(
'Thu Nov 24 15:20:52 CET 2016',
'DY MON DD HH24:MI:SS "CET" YYYY'
)
FROM DUAL
¿ there are some character to be used in the format with the meaning of "any" ? I like replace "CET" in the format string by something like "..." or "###", that is:
'DY MON DD HH24:MI:SS ... YYYY'
One example, ¿ which format will accept all the following date strings :
2017-12-31
2017_12_31
2017x12x31
You can extract relevant part by regular expression.
select
TO_DATE(
regexp_substr('Thu Nov 24 15:20:52 CET 2016', '^.+\d{2}:\d{2}:\d{2}')
||regexp_substr('Thu Nov 24 15:20:52 CET 2016', ' \d{4}$'),
'DY MON DD HH24:MI:SS YYYY')
from dual;
or
select
TO_DATE(
REGEXP_REPLACE('Thu Nov 24 15:20:52 CET 2016', '\w+ (\d{4})$', '\1'),
'DY MON DD HH24:MI:SS YYYY')
from dual;
Here's another way using to_timestamp_tz:
WITH sample_data AS (SELECT 'Thu Nov 24 15:20:52 CET 2016' str FROM dual UNION ALL
SELECT 'Fri Dec 11 23:16:39 PST 2015' str FROM dual)
SELECT str,
to_timestamp_tz(str, 'Dy Mon dd hh24:mi:ss TZR yyyy', 'nls_date_language = english') timestamp_at_orig_tz,
CAST(to_timestamp_tz(str, 'Dy Mon dd hh24:mi:ss TZR yyyy', 'nls_date_language = english') AS DATE) dt,
CAST(to_timestamp_tz(str, 'Dy Mon dd hh24:mi:ss TZR yyyy', 'nls_date_language = english') AT TIME ZONE '-5:00' AS DATE) dt_at_utc_minus_5,
cast(sys_extract_utc(to_timestamp_tz(str, 'Dy Mon dd hh24:mi:ss TZR yyyy', 'nls_date_language = english')) AS DATE) dt_at_utc
FROM sample_data;
STR TIMESTAMP_AT_ORIG_TZ DT DT_AT_UTC_MINUS_5 DT_AT_UTC
---------------------------- -------------------------------- ------------------- ------------------- -------------------
Thu Nov 24 15:20:52 CET 2016 24-NOV-16 15.20.52.000000000 CET 24/11/2016 15:20:52 24/11/2016 09:20:52 24/11/2016 14:20:52
Fri Dec 11 23:16:39 PST 2015 11-DEC-15 23.16.39.000000000 PST 11/12/2015 23:16:39 12/12/2015 02:16:39 12/12/2015 07:16:39
I've given a three different ways of converting the timestamp-as-a-string into a date:
Convert the timestamp as is into a date. Note that this will lose any timestamp information you have - useful if you really don't care about timezone information.
Convert the timestamp into a specific timezone (in my example, UTC - 5) before converting it into a date.
Convert the timestamp into UTC before converting it into a date (you can use the previous method to do this, but sys_extract_utc() is a little more self-explanatory, IMHO, which can only aid readability and maintainability!).
With your updated question (which seems to be more around the date part separator, not the timezone), the following should do the trick:
WITH sample_data AS (SELECT '2017-12-31' dt_str FROM dual UNION ALL
SELECT '2017_12_31' dt_str FROM dual UNION ALL
SELECT '2017x12x31' dt_str FROM dual)
SELECT dt_str,
to_date(regexp_replace(dt_str,
'([[:digit:]]{4}).([[:digit:]]{2}).([[:digit:]]{2})',
'\1-\2-\3'), 'yyyy-mm-dd')
FROM sample_data;
DT_STR DT
---------- ----------
2017-12-31 31/12/2017
2017_12_31 31/12/2017
2017x12x31 31/12/2017

Resources