extract date only from given date in oracle sql - sql

What is the best Oracle sql query to extract date?
input entry - 2020-10-14T07:26:32.661Z ,
expected output - 2020-10-14

If you want a DATE data type where the time component is midnight then:
SELECT TRUNC(
TO_TIMESTAMP_TZ(
'2020-10-14T07:26:32.661Z',
'YYYY-MM-DD"T"HH24:MI:SS.FF3TZR'
)
) AS truncated_date
FROM DUAL;
Which (depending on your NLS_DATE_FORMAT) outputs:
| TRUNCATED_DATE |
| :------------------ |
| 2020-10-14 00:00:00 |
(Note: a DATE data type has year, month, day, hour, minute and second components. Whatever client program you are using to access the database may choose not to show the time component but it will still be there.)
If you want a YYYY-MM-DD formatted string then:
SELECT TO_CHAR(
TO_TIMESTAMP_TZ(
'2020-10-14T07:26:32.661Z',
'YYYY-MM-DD"T"HH24:MI:SS.FF3TZR'
),
'YYYY-MM-DD'
) AS formatted_date
FROM DUAL;
| FORMATTED_DATE |
| :------------- |
| 2020-10-14 |
db<>fiddle here

The canonical way is probably trunc():
select trunc(input_entry)
This assumes that input_entry is a date or timestamp value.
EDIT:
If your input is just a string, use string operations:
select substr(input_entry, 1, 10)
You can also readily cast this to a date.

Related

Oracle SQL to delete the records except last 7 days/1 week

I am using Oracle SQL DB for ERP JD Edwards.
Dates are stored in Julian Format for this ERP.
We usually use this code to convert date from Julian to normal format.
decode(szupmj,0,' ',to_char(to_date(1900000 + olupmj,'YYYYDDD'),'MM/DD/YYYY'))
However we have a requirement where in, all data needs to be deleted except last 7 days.
Date column - 7
Please can someone help ?
SZEDUS
SZEDBT
SZUPMJ
ILPPO
74442
106108
ILPPO
74442
106108
ILPPO
77292
106109
You can convert your juliandate to a regular date and compare it to sysdate
Please test this first on a test database or use the dbfiddole to add rows with dates that are only a few days old
CREATE TABLE dates (
"SZEDUS" VARCHAR(5),
"SZEDBT" INTEGER,
"SZUPMJ" INTEGER
);
INSERT INTO dates
("SZEDUS", "SZEDBT", "SZUPMJ")
VALUES
('ILPPO', '74442', '106108');
INSERT INTO dates
("SZEDUS", "SZEDBT", "SZUPMJ")
VALUES
('ILPPO', '74442', '106108');
INSERT INTO dates
("SZEDUS", "SZEDBT", "SZUPMJ")
VALUES
('ILPPO', '77292', '106109');
SELECT * FROM dates
SZEDUS | SZEDBT | SZUPMJ
:----- | -----: | -----:
ILPPO | 74442 | 106108
ILPPO | 74442 | 106108
ILPPO | 77292 | 106109
DELETE FROM dates WHERE to_date(1900000 + SZUPMJ,'YYYYDDD') < trunc(sysdate) - 7
3 rows affected
SELECT * FROM dates
SZEDUS | SZEDBT | SZUPMJ
:----- | -----: | -----:
db<>fiddle here
Do this in a PL/SQL block where you can compute the date you want and compare withthat. It's much more efficient than applying a function to transform the Julian-formatted dates to Gregorian dates:
DECLARE
nCurr_date_minus_seven NUMBER := TO_NUMBER(TO_CHAR(SYSDATE - 7, 'YYYYDDD')) - 1900000;
BEGIN
DELETE FROM dates
WHERE SZUPMJ < nCurr_date_minus_seven;
END;
Use:
DELETE FROM table_name
WHERE szupmj < TO_CHAR(SYSDATE - INTERVAL '7' DAY, 'YYYYDDD') - 1900000;
(Which would be able to use an index on the szupmj column.)
db<>fiddle here

Delete data before most recent Sunday - SQL Snowflake

I need to delete all rows from before the most recent Sunday in a view. This can not be hard coded as the data gets updated daily. The only data I have is the date in dd-mm-yyyy format, and I have created a column with Day of Week (Monday being 1, Sunday being 7). Currently using SQL Snowflake, but any idea in any SQL types would be helpful.
TIA.
You can find the most recent Sunday by this formula:
DATE_TRUNC( 'week', current_date() ) - INTERVAL '1 DAY'
You do not need to store the day of the week as it can be easily fetched from the date. I think you store the dates in varchar as you said your dates in "dd-mm-yyyy format". I recommend you to store dates in DATE data type.
Here is a sample to delete the data before the most recent Sunday:
create or replace table test (mydate VARCHAR, dummy VARCHAR);
insert into test VALUES
('10-05-2021','willbedeleted'),
('30-10-2021','willbedeleted'),
('31-10-2021','shouldstay'), -- most recent sunday
('01-11-2021','shouldstay'),
('02-11-2021','shouldstay'),
('03-11-2021','shouldstay');
delete from test where TO_DATE( mydate, 'DD-MM-YYYY' ) <
DATE_TRUNC( 'week', current_date() ) - INTERVAL '1 DAY';
select * from test;
+------------+------------+
| MYDATE | DUMMY |
+------------+------------+
| 31-10-2021 | shouldstay |
| 01-11-2021 | shouldstay |
| 02-11-2021 | shouldstay |
| 03-11-2021 | shouldstay |
+------------+------------+

Storing Dates In SQL: Having Some Trouble

I have a bunch of data sitting in a Postgres database for a website I am building. Problem is, I don't really know what I should do with the date information. For example, I have a table called events that has a date column that stores date information as a string until I can figure out what to do with it.
The reason they are in string format is unfortunately for the topic of my website, their is not a good API, so I had to scrape data. Here's what some of the data looks like inside the column:
| Date |
|-------------------------------------|
| Friday 07.30.2021 at 08:30 AM ET |
| Wednesday 04.07.2021 at 10:00 PM ET |
| Saturday 03.27.2010 |
| Monday 01.11.2010 |
| Saturday 02.09.2019 at 05:00 PM ET |
| Wednesday 03.31.2010 |
It would have been nice to have every row have the time with it, but a lot of entries don't. I have no problem doing some string manipulation on the data to get them into a certain format where they can be turned into a date, I am just somewhat stumped on what I should do next.
What would you do in this situation if you were restricted to the data seen in the events table? Would you store it as UTC? How would you handle the dates without a time? Would you give up and just display everything as EST dates regardless of where the user lives (lol)?
It would be nice to use these dates to display correctly for anyone anywhere in the world, but it looks like I might be pigeonholed because of the dates that don't have a time associated with them.
Converting your mishmash free form looking date to a standard timestamp is not all that daunting as it seems. Your samples indicate you have a string with 5 separate pieces of information: day name, date (mm.dd.yyyy), literal (at),time of day, day part (AM,PM) and some code for timezone each separated by spaces. But for them to be useful the first step is splitting this into the individual parts. For that us a regular expression to ensure single spaces then use string_to_array to create an array of up to 6 elements. This gives:
+--------------------------+--------+----------------------------------+
| Field | Array | Action / |
| | index | Usage |
+--------------------------+--------+----------------------------------+
| day name | 1 | ignore |
| date | 2 | cast to date |
| literal 'at' | 3 | ignore |
| time of day | 4 | interval as hours since midnight |
| AM/PM | 5 | adjustment for time of day |
| some code for timezone | 6 | ??? |
+--------------------------+--------+----------------------------------+
Putting it all together we arrive at:
with test_data ( stg ) as
( values ('Friday 07.30.2021 at 08:30 AM ET' )
, ('Wednesday 04.07.2021 at 10:00 PM ET')
, ('Saturday 03.27.2010' )
, ('Monday 01.11.2010' )
, ('Saturday 02.09.2019 at 05:00 PM ET' )
, ('Wednesday 03.31.2010')
)
-- <<< Your query begins here >>>
, stg_array( strings) as
( select string_to_array(regexp_replace(stg, '( ){1,}',' ','g'), ' ' )
from test_data --<<< your actual table >>>
) -- select * from stg_array
, as_columns( dt, tod_interval, adj_interval, tz) as
( select strings[2]::date
, case when array_length(strings,1) >= 4
then strings[4]::interval
else '00:00':: interval
end
, case when array_length(strings,1) >= 5 then
case when strings[5]='PM'
then interval '12 hours'
else interval '0 hours'
end
else interval '0 hours'
end
, case when array_length(strings,1) >= 6
then strings[6]
else current_setting('TIMEZONE')
end
from stg_array
)
select dt + tod_interval + adj_interval dtts, tz
from as_columns;
This gives the corresponding timestamp for date, time, and AM/PM indicator (in the current timezone) for items without a timezone specifies. For those containing a timezone code, you will have to convert to a proper timezone name. Note ET is not a valid timezone name nor a valid abbreviation. Perhaps a lookup table. See example here; it also contains a regexp based solution. Also the example in run on db<>fiddle. Their server is in the UK, thus the timezone.

How to group sum results by date with custom start time PostrgresQL

I am trying to group my sum results by custom day in Postgresql.
As regular day starts at 00:00 , I would like mine to start at 04:00am , so if there is entry with time 2019-01-03 02:23 it would count into '2019-01-02' instead.
Right now my code looks like this:
Bottom part works perfectly on day type 00:00 - 23.59 , however I would like to group it by my upper range created above. I just don't know how to connect those two parts.
with dateRange as(
SELECT
generate_series(
MIN(to_date(payments2.paymenttime,'DD Mon YYYY')) + interval '4 hour',
max(to_date(payments2.paymenttime,'DD Mon YYYY')),
'24 hour') as theday
from payments2
)
select
sum(cast(payments2.servicecharge as money)) as total,
to_date(payments2.paymenttime,'DD Mon YYYY') as date
from payments2
group by date
Result like this
+------------+------------+
| total | date |
+------------+------------+
| 20 | 2019-01-01 |
+------------+------------+
| 60 | 2019-01-02 |
+------------+------------+
| 35 | 2019-01-03 |
+------------+------------+
| 21 | 2019-01-04 |
+------------+------------+
Many thanks for your help.
If I didn't misunderstand your question, you just need to subtract 4 hours from the timestamp before casting to date, you don't even need the CTE.
Something like
select
sum(cast(payments2.servicecharge as money)) as total,
(to_timestamp(payments2.paymenttime,'DD Mon YYYY HH24:MI:SS') - interval '4 hours')::date as date
from payments2
group by date
Yu may need to use a different format in the to_timestamp function depending on the format of the payments2.paymenttime string

How to add number field to varchar field in oracle. varchar field is time in HH:MM:SS format

I have 2 columns
Start_Time (VARCHAR) Duration_in_sec (NUMBER)
12:03:11 220
11:05:33 345
I want to add col2 to col1 to get end time.
How do I do that I tried
Select TO_NUMBER(col1)+col2 as end_time from ABC.
This is giving me error. Can someone help me with right way.
This is the error I am getting which is straight forward but How to get the right number. I need to do some maniculation on col1 but I am not getting how to do that
ORA-01722: invalid number
01722. 00000 - "invalid number"
*Cause: The specified number was invalid.
*Action: Specify a valid number.
You can convert the start time and duration both to intervals and then add the two intervals. That way you can handle when the duration is more than one day:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE table_name ( Start_Time, Duration_in_sec ) AS
SELECT '12:03:11', 220 FROM DUAL UNION ALL
SELECT '11:05:33', 345 FROM DUAL UNION ALL
SELECT '23:59:59', 1 FROM DUAL;
Query 1:
SELECT t.*,
( TO_TIMESTAMP( start_time, 'HH24:MI:SS' )
- TO_TIMESTAMP( '00:00:00', 'HH24:MI:SS' )
) + NUMTODSINTERVAL( Duration_in_sec, 'SECOND' ) AS end_time
FROM table_name t
Results:
| START_TIME | DURATION_IN_SEC | END_TIME |
|------------|-----------------|--------------|
| 12:03:11 | 220 | 0 12:6:51.0 |
| 11:05:33 | 345 | 0 11:11:18.0 |
| 23:59:59 | 1 | 1 0:0:0.0 |
A better solution would be to store both the start time and duration as intervals; then you can just add the values:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE table_name (
start_time INTERVAL DAY TO SECOND,
duration_in_sec INTERVAL DAY TO SECOND
);
INSERT INTO table_name
SELECT INTERVAL '12:03:11' HOUR TO SECOND, INTERVAL '220' SECOND FROM DUAL UNION ALL
SELECT INTERVAL '11:05:33' HOUR TO SECOND, INTERVAL '345' SECOND FROM DUAL UNION ALL
SELECT INTERVAL '23:59:59' HOUR TO SECOND, INTERVAL '1' SECOND FROM DUAL;
Query 1:
SELECT t.*,
start_time + Duration_in_sec AS end_time
FROM table_name t
Results:
| START_TIME | DURATION_IN_SEC | END_TIME |
|--------------|-----------------|--------------|
| 0 12:3:11.0 | 0 0:3:40.0 | 0 12:6:51.0 |
| 0 11:5:33.0 | 0 0:5:45.0 | 0 11:11:18.0 |
| 0 23:59:59.0 | 0 0:0:1.0 | 1 0:0:0.0 |
Here's one way, using the numToDSInterval function:
select
to_date(start_time,'HH24:MI:SS')+numToDSInterval(Duration_in_sec, 'second')
from time_seconds;
(Uses a table named "time_seconds")
To return just the time string:
select
to_char(
to_date(start_time,'HH24:MI:SS')+numToDSInterval(Duration_in_sec, 'second'), 'HH24:MI:SS'
)
from time_seconds;
So first, it converts the timestamp from a VARCHAR to a DATE, then it increments that DATE by your number of seconds using numToDSInterval.
I would add, though, that storing a time as a string seems like a bad idea. And are your times storied in 24-hour format? Or 12-hour with AM/PM? How can you be sure that your strings are parseable into Dates?
Etc.? You should be using the right datatypes for the right job.