How to specify my table to insert data as time in minutes and second into a column? - sql

I have been struggling for several hours now and I have not been able to find the solution to my problem. This is an assignment, but I am stuck in this part.
CREATE TABLE Trip
(
Trip_Id SERIAL,
Origin VARCHAR(50) NOT NULL,
Destination VARCHAR(50) NOT NULL,
Date_Time_Picked TIMESTAMP NOT NULL DEFAULT CURRENT_DATE,
Estimated_Time TIME NOT NULL DEFAULT CURRENT_TIME,
Price DECIMAL NOT NULL,
PRIMARY KEY(Trip_Id)
);
INSERT INTO Trip (Origin, Destination, Estimated_Time, Price )
VALUES ('Hialeah' ,'Miami Beach', 30:00, 40.00);
The insert statement in postgreSQL shows a error because the time format. The column Estimated_Time is supposed to store the time in minutes and seconds, but the compiler shows an error because interprets 30:00 as hours and seconds. How can I handle the input of the user to save 30:00 as 30 minutes and 0 seconds. The Trip table can be modified, obviously, the insert statement requires a conversion or cast from '30:00' to Time type, but I am lost in how to do it. Unfortunately, books do not explain how this is done. I would greatly appreciate any hint or example. Thanks in advance.

as pointed out by a_horse_with_no_name and jarlh,
Estimated_Time is the duration of the trip, so the format should be interval
CREATE TABLE trip (
trip_id SERIAL,
origin VARCHAR(50) NOT NULL,
destination VARCHAR(50) NOT NULL,
date_time_picked TIMESTAMP WITHOUT TIME ZONE DEFAULT 'now'::text::date NOT NULL,
estimated_time INTERVAL,
price NUMERIC NOT NULL,
CONSTRAINT trip_pkey PRIMARY KEY(trip_id)
)
and the insert sould be
INSERT INTO Trip (Origin, Destination, Estimated_Time, Price )
VALUES ('Hialeah' ,'Miami Beach', '00:30:00', 40.00);

Related

Subtract two timestamptz values and insert the result into a third column

I have the following table:
CREATE TABLE duration
(
departure_time TIMESTAMPTZ,
arrival_time TIMESTAMPTZ,
duration TIME NOT NULL, -- Not sure about the datatype..
flight_id INT UNIQUE NOT NULL,
CHECK (scheduled_duration > 0),
CHECK (scheduled_arrival_time > scheduled_departure_time),
FOREIGN KEY (flight_id) REFERENCES flight(flight_id),
PRIMARY KEY (scheduled_departure_time, scheduled_arrival_time)
);
I want to calculate arrival_time - departure_time and then insert the result into the column duration. Preferably, the result of the duration subtraction would be 6h 30m. I am new to databases and PostgreSQL and I can't find a way to calculate a subtraction of two timestamps, taking into consideration their timezones at the same time.
Use a generated column
CREATE TABLE duration
(
departure_time TIME WITH TIME ZONE,
arrival_time TIME WITH TIME ZONE,
scheduled_duration INT,
flight_id INT,
duration2 TIME GENERATED ALWAYS AS ("arrival_time"::time - "departure_time"::time) STORED,
CHECK (scheduled_duration > 0),
CHECK (arrival_time > departure_time),
FOREIGN KEY (flight_id) REFERENCES flight(flight_id),
PRIMARY KEY (departure_time, arrival_time)
);
SELECT
EXTRACT(EPOCH FROM '2022-07-07 15:00:00.00000'::TIMESTAMP - '2022-07-07 15:00:00.00000'::TIMESTAMP)

Timezone aware uniqueness constraint

I am working with a timesheet app, used by users from multiple timezones. I am trying to introduce a unique constraint, that only allows users to clock_in or clock_out once per day in the local timezone.
Please refer to the following table declaration:
Table "public.entries"
---------------------------------------------
Column | Type |
---------------------------------------------
id | bigint |
user_id | bigint |
entry_type | string | enum(clock_in, clock_out)
created_at | timestamp(6) without time zone |
But little lost on how to handle the timezone-aware uniqueness.
Update:
I am considering 0:00 hrs to 23:55 hrs of local time zone as day.
User's timezone is stored in the users table but can move to the entries table if it helps with constraints.
I misread the question and wrote a bad answer, so here's a new one...
I assume this is a typical client-server-db setup. You need to obtain the local time zone from the client that's clocking in/out the user; Postgres doesn't know what it is. We'll figure out the user's local date from that and store it. Then we'll have a uniqueness index on <user, local date>.
I thought there'd be fancier ways to do this by storing the timestamptz with a separate time zone col and calculating the date within the uniqueness index, but Postgres doesn't allow us to use date_trunc within an index. So we're going to denormalize just a little and make things a lot easier with this additional date col.
CREATE TABLE clock_in (
user_id bigint NOT NULL,
created_at timestamptz NOT NULL, -- stores microseconds since epoch
local_date date NOT NULL, -- stores the <year, month, day> in whatever timezone the user clocked in from
-- optional for bookkeeping purposes: time_zone text NOT NULL,
UNIQUE(user_id, local_date)
);
Take a look at the official date/time type docs for further explanation of the above. IMO you shouldn't rely on DB constraints to reject bad user input. They're more of a second line of defense meant to ensure a self-consistent database. First your server should query the last clock-in and error out if it was in the same day, and also error if there was no clock-in that day. You'll be able to yield more useful error messages that way. Then you can insert...
INSERT INTO clock_in(user_id, created_at, local_date) (
SELECT 1, now(),
(date_trunc('day', now() AT TIME ZONE 'insert_users_timezone_here'))::date
);
Usage example for a client who has indicated it's in the PST timezone:
me=# CREATE TABLE clock_in ( user_id bigint NOT NULL, created_at timestamptz NOT NULL, local_date date NOT NULL, UNIQUE(user_id, local_date) );
CREATE TABLE
me=# INSERT INTO clock_in(user_id, created_at, local_date) ( SELECT 1, now(), (date_trunc('day', now() AT TIME ZONE 'PST'))::date );
INSERT 0 1
me=# INSERT INTO clock_in(user_id, created_at, local_date) ( SELECT 1, now(), (date_trunc('day', now() AT TIME ZONE 'PST'))::date );
ERROR: duplicate key value violates unique constraint "clock_in_user_id_local_date_key"
DETAIL: Key (user_id, local_date)=(1, 2022-04-13) already exists.
me=# INSERT INTO clock_in(user_id, created_at, local_date) ( SELECT 1, now(), (date_trunc('day', now() AT TIME ZONE 'PST' + interval '10' hour))::date );
INSERT 0 1
me=#
Then you'd do the same for clock-outs.
Using timestamptz instead of timestamp is deliberate. You should almost never use timestamp, for reasons other answers describe well.
Firstly, you'll probably want to use a native datetime datatype and a range one at that, e.g. tstzrange (with timezone) / tsrange (without timezone) – they allow you to natively store a start and end time – see https://www.postgresql.org/docs/current/rangetypes.html#RANGETYPES-BUILTIN
You can optionally add an exclusion constraint to ensure no two shifts overlap – see: https://www.postgresql.org/docs/current/rangetypes.html#RANGETYPES-CONSTRAINT if that's all you really want to ensure, then that might be enough.
If you definitely want to ensure there's only one shift starting or ending per day, you can use a function to derive a unique index:
create unique index INDEX_NAME on TABLE_NAME (date_trunc('day', lower(column_name)))
For your example specifically:
create unique index idx_unique_shift_start_time on entries (user_id, date_trunc('day', lower(active_during)))
create unique index idx_unique_shift_end_time on entries (user_id, date_trunc('day', upper(active_during)))
These two indexes take the lower or upper bounds of the range (i.e. the start time or end time), then truncate to just the day (i.e. drop the hours, minutes, seconds etc) and then combine with the user_id to give us a unique key.

Time period SQL view

I am trying to create a SQL view on two tables I'm working with:
CREATE TABLE availability
(
doctor varchar(20) NOT NULL,
avl_date date NOT NULL,
avl_start time NOT NULL,
avl_end time NOT NULL,
CONSTRAINT pk_availability PRIMARY KEY (doctor, avl_date)
);
And
CREATE TABLE appointments
(
patient varchar(20) NOT NULL,
doctor varchar(20) NOT NULL,
apt_date date NOT NULL,
apt_start time NOT NULL,
apt_end time NOT NULL,
CONSTRAINT pk_appointments PRIMARY KEY (patient, apt_date)
);
The view I am trying to create lists all maximal time periods (apt date, apt start, apt end) during which no further appointments are possible (consider doctors’ availability as well).
Any help is greatly appreciated, thanks.
this should work if the appointment time-duration is constant. You can optimize it by re-writing with inner join instead of minus. Also, remember to put availability table above minus.
select doctor
, avl_date
, avl_start
, avl_end
from availability
minus
select doctor
, apt_date
, apt_start
, apt_end
from appointments

I just want to use time in this colum (novice)

CREATE TABLE Vitals
(
Record_No int NOT NULL PRIMARY KEY,
Patient_ID int,
Date_Taken DATE NOT NULL,
Time_Taken time(7) NOT NULL,
Systolic int,
CHECK (Systolic > Diastolic),
Diastolic int,
Heart_Rate int CHECK (Heart_Rate > 30),
CONSTRAINT fk_Patient_ID FOREIGN KEY (Patient_ID) REFERENCES Patients(Patient_ID)
);
But, I get an error
Time_Taken time(7) NOT NULL,
*
ERROR at line 6:
ORA-00907: missing right parenthesis
When I use timestamp default systimestamp
it works. All I need is the time for Time_Taken column.
enter image description here
You can not use the TIME data type as it is not the oracle defined datatype.
You can use the DATE or TIMESTAMP data type to store time with date.
Supported data types in oracle are documented here.

ORA-00984 Column not allowed here with date in oracle SQL

Im inserting values into this table
CREATE TABLE Flight (
FlightNumber char(7) primary key,
ArrivalAirportCode char(6) references Airport (Airport_code),
DepartureAirportCode char(6) references Airport (Airport_code),
AircraftNumber varchar2(25) references Aircraft (AircraftNumber),
ArrivalDate date,
ArrivalTime Varchar2(5),
DepartureDate date,
DepartureTime varchar2(5)
);
and here are the values Im inserting into it
INSERT INTO FLIGHT values
('CA3048',
'LHR',
'EDI',
'N859E',
'14-NOV-2014',
'22:15',
'14-NOV-2014',
'20:15');
And I get the column not allowed here error for the 2nd date I insert, but not the first one. I've tried putting quotes around the date but I just get another error.
'14-NOV-2014'
Why are you inserting a string in a DATE column? '14-NOV-2014' is a STRING and NOT a DATE. You should not depend on implicit data type conversion.
Always, convert the string into a DATE explicitly using TO_DATE and proper format mask.
For example,
TO_DATE('14-NOV-2014','DD-MON-YYYY')
One more thing,
DepartureTime varchar2(5)
Makes no sense. You already have a DATE column, a DATE would have the time element too.
No need of a separate time column. A DATE has both date and time elements stored in 7 bytes.
Oracle stores DATE in total of 7 bytes. Each byte in it stores values for an element of the DATE as follows:
Byte Description
---- ------------------------------------------------
1 Century value but before storing it add 100 to it
2 Year and 100 is added to it before storing
3 Month
4 Day of the month
5 Hours but add 1 before storing it
6 Minutes but add 1 before storing it
7 Seconds but add 1 before storing it
All you need to do is just have 2 DATE columns:
CREATE TABLE Flight (
FlightNumber char(7) primary key,
ArrivalAirportCode char(6) references Airport (Airport_code),
DepartureAirportCode char(6) references Airport (Airport_code),
AircraftNumber varchar2(25) references Aircraft (AircraftNumber),
ArrivalDate date,
DepartureDate date
);
And then insert the values as:
INSERT INTO FLIGHT values
('CA3048',
'LHR',
'EDI',
'N859E',
TO_DATE('14-NOV-2014 22:15:00','DD-MON-YYYY HH24:MI:SS'),
TO_DATE('14-NOV-2014 20:15:00','DD-MON-YYYY HH24:MI:SS')
);
Update
As mentioned in the comments by #GriffeyDog and #a_horse_with_no_name.
Alternatively, you could also the ANSI literal instead, for example:
timestamp '2014-11-14 22:15'