Date time format in Oracle - sql

I am new to sql and am working on an example. Among the tables I have created, I have the comments table:
CREATE TABLE comments (
club VARCHAR2(60) NOT NULL,
nick VARCHAR2(35),
msg_date DATE,
title VARCHAR2(100) NOT NULL,
director VARCHAR2(50) NOT NULL,
subject VARCHAR2(100),
message VARCHAR2(1500),
valoration NUMBER(2),
CONSTRAINT PK_COMMENTS PRIMARY KEY (nick,msg_date),
CONSTRAINT FK_COMMENTS_MEMBER FOREIGN KEY (nick,club) REFERENCES membership ON DELETE CASCADE,
CONSTRAINT FK_COMMENTS_MOVIES FOREIGN KEY (title,director) REFERENCES movies,
CONSTRAINT CK_COMMENTS_VAL CHECK (valoration<11)
);
I am asked to create a trigger that does the following:
if a comment arrives on the same date as another one already stored, register it with the date
'one second later'.
The problem I have is that I do not know how to convert the 'one second' later into a date. Any idea on how to solve this problem?

msg_date + interval '1' second
or alternatively msg_date + (1/(24*60*60))
However this whole scenario is fraught with danger. While checking existing messages in the table within the trigger the table may be changing in other transactions and so there is a real risk of race conditions here - two messages both adding 1 second to an existing message will then have the same date. This would be the case whether the check was in a trigger or application code.
If this is a real world scenario I would avoid the trigger, use a timestamp rather than date, where the precision is between millis and nanos and consider how to deal with the lower risk of messages with the same timestamp as a business problem - what is the implication if it does occur.

Related

Create primary key for table with period (Temporal Validity) in Oracle SQL

I have a question regarding to primary key for Oracle Table with Period.
I have created two tables like following:
create table el_temporal_try( -- Parent Table
id number(10) not null,
ColumnA varchar(10),
constraint el_temporal_try_pk primary key (id),
period for valid_period
);
create table el_temporal_try_son( -- Son Table
id number(10) not null,
ColumnA varchar(10),
parent_id number(10),
constraint el_temporal_try_FY foreign key (parent_id) references el_temporal_try(id),
period for valid_period
);
This script gone through successfully. However I have problem with inserting data:
I have executed following two insert statements into the parent table:
1st: statement
insert into el_temporal_try
(id, columnA,valid_period_start, valid_period_end)
values
(1,'A',sysdate - 10, sysdate - 9);
Result:
1 row inserted.
2nd: statement
insert into el_temporal_try
(id, columnA,valid_period_start, valid_period_end)
values
(1,'B',sysdate - 8, sysdate - 7);
Result
ORA-00001: unique constraint (PBSVW.EL_TEMPORAL_TRY_PK) violated
I understand it is because of the "ID" column. However, my issues because this two rows are for a different period, should it be allowed?
I was intended to use this period for feature to capture the change history of a record as an alternative to flashback. However, does it means that I should not use primary key at this situation?
Thanks in advance!
The problem is related to the id column like you said. it's not possible to add the registry because the primary key is unique, then your second insert statement references the same ID from the first. You need to change the ID always you insert a line.
On Oracle 12c, you can use the identity like this link.
https://www.oracletutorial.com/oracle-basics/oracle-identity-column/
in another version, you can use sequence and trigger to do this.
https://chartio.com/resources/tutorials/how-to-define-an-auto-increment-primary-key-in-oracle/
Thanks for everyone's help on this. This most likely means I cannot use Primary Key/Foreign Key to maintain the referential integrity between the parent and son for my situation within a particular timestamp, but I have to go for something else.
Thanks a lot!

Inserting values into the table

I'm trying to insert the values into the tables that I created.
This is the values that I'm trying to insert.
INSERT INTO DDR_Rental (customer_ID, rental_date, rent_fee, film_title, start_date, expiry_date, rating)
VALUES (12345, '12-Mar-19', '4.99', 'Peppermint', '12-Mar-19', '22-Mar-19', 4);
This is the datatypes and the constraints.
CREATE TABLE DDR_Rental
(customer_ID NUMBER(5),
rental_date DATE,
rent_fee NUMBER(3,2) CONSTRAINT SYS_RENTAL_FEE_NN NOT NULL,
film_title VARCHAR2(20),
start_date DATE,
expiry_date DATE,
rating NUMBER(5),
CONSTRAINT SYS_RENTAL_PK PRIMARY KEY ((customer_ID), (rental_date), (film_title)),
CONSTRAINT SYS_RENTAL_CUS_ID_FK1 FOREIGN KEY (customer_ID) REFERENCES
DDR_CUSTOMER(CUSTOMER_ID),
CONSTRAINT SYS_RENTAL_FILM_TITLE_FK2 FOREIGN KEY (film_title) REFERENCES
DDR_MOVIE_TITLE(FILM_TITLE),
CONSTRAINT SYS_RENTAL_EXP_DATE_CK CHECK (expiry_date >= start_date),
CONSTRAINT SYS_RENTAL_START_DATE_CK CHECK (start_date >= rental_date),
CONSTRAINT SYS_RENTAL_RATING_CK CHECK (REGEXP_LIKE(rating,('[12345]'))));
The error says unique constraint (CPRG250.SYS_RENTAL_PK) violated
It seems like you are trying to add a duplicate rental event for the same film by the same customer on exact one day. That can obviously happen, if you allow in your business logic the situation that a customer can rent a movie, give it back on the same day and rent it back again.
Knowing your business, you have 2 ways to deal with this situation:
Your business model don't allow that. This means that this is a duplicate record and you shouldn't add currently existing record, in which case showing that error is perfectly fine and doesn't allow for duplicates, since this event happened only once.
Your business model allows that. In this case, you should modify your rental_date column to store time along with the date, instead of only storing date, so that you know when the rental event actually happen. You could use datetime type for example to store date with time. This can be done when creating your table, just replace rental_date date with rental_date datetime. If the table is already created you will need to drop and recreate PRIMARY KEY and then after that you could change type of your column using ALTER TABLE ddr_rental ALTER COLUMN rental_date datetime and re-create the primary key. Check values stored in your table after that, since 2019-01-01 will now be represented as 2019-01-01 00:00:00.000 appending the time which wasn't specified before.
In addition to (1) you could wrap your code and handle this exception to return a clear message when this happens, showing that the movie has already been rented.
Moreover, since you don't have a table for storing movies in your inventory, this can lead to a possible mistake, since you may have more than 1 copy of a movie. In this case I suggest that you create separate film and film_copy tables to properly identify which copy of a film has been rented, so that you can rent another copy.
You have a unique constraint in your table. Your table already has a record with customer_id, rental_date and film_title that you want to insert.
Try this query and you will see that there already is a record
select * from DDR_Rental
where customer_id=12345 and rental_date='12-Mar-19' and film_title='Peppermint'

Getting 'ORA-00907: missing right parenthesis', cannot find error

After running my script in Oracle I am getting the error code 'ORA-00907: missing right parenthesis'. I have already created the client and employee tables, both of which ran correctly and added the tables alright. However I am getting an issue creating my 'Appointment' table. The code for creating it is below:
create table Appointment
(appointment_num number(9) not null primary key,
appointment_time datetime(),
emp_ID number(4) not null references employee (emp_ID),
client_ID number(9) not null references client (client_ID))
Cannot find where the error is and cannot find any troubleshooting guidance elsewhere for the issue. Any help would be appreciated.
you have datetime(), change it to date. Please note that datetime is invalid datatype on oracle, you need to use date or alternatively timestamp
create table Appointment
(
appointment_num number(9) not null primary key,
appointment_time date,
emp_ID number(4) not null references employee (emp_ID),
client_ID number(9) not null references client (client_ID)
)
Diference between Date and Timestamp
One of the main problems with the DATE datatype was its' inability to be granular enough to determine which event might have happened first in relation to another event. Oracle has expanded on the DATE datatype and has given us the TIMESTAMP datatype which stores all the information that the DATE datatype stores, but also includes fractional seconds.
Reference

Enforcing existence of many-to-many and one-to-one information at the same time (PostgreSQL)

The relation "astronomers discover stars" is m:n, as an astronomer can discover many stars, and a star can as well be discovered from many astronomers.
In any case however, there will be a single date of discovery for every star. If there are many astronomers working on it, they should do it simultaneously, otherwise, only the first one gets the credits of the discovery.
So, in PostgreSQL, we have:
CREATE TABLE astronomer (
astronomer_id serial PRIMARY KEY,
astronomer_name text NOT NULL UNIQUE
);
CREATE TABLE star (
star_id serial PRIMARY KEY,
star_name text NOT NULL UNIQUE,
discovery_date date
);
CREATE TABLE discovered (
star_id integer NOT NULL REFERENCES star,
astronomer_id integer NOT NULL REFERENCES astronomer,
CONSTRAINT pk_discovered PRIMARY KEY (star_id, astronomer_id)
);
Now the trick question: I want to enforce that whenever there is a discovery date for a star, there will be at least one entry in the discovered table, and vice versa.
I could create a unique key (star_id, discovery_date) in table star, and then use that combination as a foreign key in table discovered instead of the star_id. That would solve half of the problem, but still leave it possible to have a discovered_date without astronomers for it.
I can't see any simple solution other than using a trigger to check, or only allowing data enter via a stored procedure that will at the same time insert a discovery_date and entries into the table discovered.
Any ideas?
Thank you!
I would just move the discovery_date column to the discovered table
CREATE TABLE astronomer (
astronomer_id serial PRIMARY KEY,
astronomer_name text NOT NULL UNIQUE
);
CREATE TABLE star (
star_id serial PRIMARY KEY,
star_name text NOT NULL UNIQUE
);
CREATE TABLE discovered (
star_id integer NOT NULL REFERENCES star,
astronomer_id integer NOT NULL REFERENCES astronomer,
discovery_date date not null,
CONSTRAINT pk_discovered PRIMARY KEY (star_id, astronomer_id)
);
Now you have the problem of multiple dates for the same star but as you say in the question only the first one(s) will get the credit.

Check constraint for making sure a date is not in the future?

I'm trying to create a table that records changes made to the estimated hours of another table. The database as a whole is a project management system for a company to assign work to its employees and create invoices for the customer.
Currently I have:
CREATE TABLE task_history
(
task_history_id NUMBER(5),
previous_est_hours NUMBER(3,1),
change_date DATE,
reason_for_change VARCHAR2(50),
task_id NUMBER(5),
CONSTRAINT TASKHIST_TASKHISTID_PK PRIMARY KEY (task_history_id),
CONSTRAINT TASKHIST_TASKID_FK FOREIGN KEY (task_id) REFERENCES task(task_id),
CONSTRAINT TASKHIST_TASKID_NN CHECK (task_id IS NOT NULL),
CONSTRAINT TASKHIST_CHANGEDATE_NONFUTURE CHECK (change_date <= sysdate)
);
change_date must not be a future date, it must be either today or in the past.
The last check constraint is the problem. As I understand it you cannot use the sysdate because of a reason I've forgotten, but you can't. I've also tried GETDATE() and every other variant I've found online.
How can I do this presumably simple task?
You can't call a function from a check constraint so the most natural approach would be to define a trigger on the task_history table, i.e.
CREATE OR REPLACE TRIGGER task_change_date_in_past
BEFORE INSERT OR UPDATE ON task_history
FOR EACH ROW
BEGIN
IF( :new.change_date > sysdate )
THEN
RAISE_APPLICATION_ERROR( -20001, 'Change date must be in the past' );
END IF;
END;
CONSTRAINT TASKHIST_CHANGEDATE_NONFUTURE CHECK (change_date <= sysdate)
This would be really problematic as a Constraint.
Consider what would happen if one were to update a row (some other column) or deactivate/reactive the constraint. It won't check against the original date, but against the current SYSDATE!
I would advocate adding another column to the table, defaulted to SYSDATE, and building a constraint or TRIGGER that will compare the new column (stored SYSDATE) against change_date.
In 11g, it is possible to use check constraints with sysdate: http://rwijk.blogspot.com/2007/12/check-constraints-with-sysdate.html
Prior to 11g you should use Justin's approach.
Regards,
Rob.
I think that you can define a user-defined function that performs that check and use it in the check constraint.