Oracle - How do I create a table that has an autoincrementing unique key for the ID - sql

This is the first time that I've use oracle SQL and I'm having a problem creating tables with a unique key.
I don't understand why this auto-incrementing id is not working:
ID BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY
The next question I have is why I am getting an error in each of my statements:
ORA-00922: missing or invalid option
Here is my code:
--
-- Sequence for aout incrment
--
CREATE SEQUENCE IF NOT EXISTS AUTO_INC_SEQ
START WITH 1
INCREMENT BY 1;
--
-- Table Person
--
CREATE TABLE IF NOT EXISTS FITNESS_PERSON
(
ID NUMBER NOT NULL PRIMARY KEY,
FIRST_NAME VARCHAR NOT NULL,
LAST_NAME VARCHAR NOT NULL,
NICK_NAME VARCHAR NOT NULL,
DATE_BIRTH DATE NOT NULL,
PASSWORD VARCHAR NOT NULL,
CONSTRAINT UNIQUE(NICK_NAME)
);
--
-- Table BMR
--
CREATE TABLE IF NOT EXISTS FITNESS_BMR
(
ID NUMBER NOT NULL PRIMARY KEY,
VALUE FLOAT NOT NULL,
VALUE_DATE DATE NOT NULL
);
--
-- M:N for BMR and Person
--
CREATE TABLE IF NOT EXISTS FITNESS_BMR_PERSON
(
BMR_ID NUMBER NOT NULL,
PERSON_ID NUMBER NOT NULL,
FOREIGN KEY(BMR_ID) REFERENCES FITNESS_BMR(ID),
FOREIGN KEY(PERSON_ID) REFERENCES FITNESS_PERSON(ID),
CONSTRAINT BMR_PER PRIMARY KEY(BMR_ID, PERSON_ID)
);
What's the right way to do this (create a table and with an auto-incrementing key that is unique).

You can use a table, a sequence to generate unique ID values and a trigger.
For example:
Table:
CREATE Table FITNESS_BMR
(
ID NUMBER NOT NULL PRIMARY KEY,
VALUE FLOAT NOT NULL,
VALUE_DATE DATE NOT NULL
);
Sequence: create sequence t1_seq start with 1 increment by 1 nomaxvalue;
Trigger:
CREATE OR REPLACE TRIGGER test_trigger
BEFORE INSERT
ON FITNESS_BMR
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
SELECT t1_seq.nextval INTO :NEW.ID FROM dual;
END;
/

Related

PostgreSQL- Function to update 3 shared columns from table B on table A

The main goal is to create a function that updates 3 columns on another table (the update of the 2 columns from the first table trigger the update on the second one).
CREATE TRIGGER trigger_modif_amount
AFTER INSERT OR DELETE OR UPDATE OF net_amount, iva_amount
ON erp.tb_lines
FOR EACH ROW
EXECUTE PROCEDURE modif_amount();
The name of the 3 columns: net_amount , iva_amount , total_amount
CREATE OR REPLACE FUNCTION modif_amount()
RETURNS TRIGGER AS $$
BEGIN
UPDATE erp.tb_invoice
SET (net_amount,iva_amount,tot_amount) = (select COALESCE(sum(net_amount),0),COALESCE(sum(iva_amount),0),COALESCE(sum(net_amount+iva_amount),0) from tb_lines where invoice_id = coalesce(NEW.invoice_id, OLD.invoice_id))
WHERE invoice_id = coalesce(NEW.invoice_id, OLD.invoice_id);
END;
$$ LANGUAGE plpsql;
The tables:
CREATE TABLE erp.tb_invoice (
co_code CHARACTER(3) NOT NULL,
invoice_id INT NOT NULL,
invoice_no CHARACTER VARYING(15) NOT NULL,
cust_no CHARACTER(5) NOT NULL,
site_id INT NOT NULL,
payed CHARACTER(1) NOT NULL DEFAULT 'N',
net_amount REAL NOT NULL,
iva_amount REAL NOT NULL,
tot_amount REAL NOT NULL,
last_updated_by CHARACTER VARYING(20) DEFAULT 'SYSTEM',
last_update_date DATE NOT NULL,
CONSTRAINT pk_invoice PRIMARY KEY (invoice_id),
CONSTRAINT fk_invoice_company FOREIGN KEY (co_code) REFERENCES erp.tb_company (co_code),
CONSTRAINT fk_invoice_customer FOREIGN KEY (cust_no) REFERENCES erp.tb_customer (cust_no),
CONSTRAINT fk_invoice_site FOREIGN KEY (site_id) REFERENCES erp.tb_site (site_id)
);
CREATE TABLE erp.tb_lines (
invoice_id INT NOT NULL,
line_id INT NOT NULL,
line_num INT NOT NULL,
item CHARACTER(5),
description CHARACTER VARYING(120) NOT NULL,
net_amount REAL NOT NULL,
iva_amount REAL NOT NULL,
last_updated_by CHARACTER VARYING(20) DEFAULT 'SYSTEM',
last_update_date DATE NOT NULL,
CONSTRAINT pk_lines PRIMARY KEY (line_id),
CONSTRAINT fk_lines_invoice FOREIGN KEY (invoice_id) REFERENCES erp.tb_invoice (invoice_id)
);
This trigger definition to only execute the function (modif_amount) if column (net_amount) and (iva_amount) is specified as a target in the UPDATE command:
CREATE TRIGGER trigger_modif_amount
AFTER UPDATE OF net_amount,iva_amount ON tb_lines
FOR EACH ROW
EXECUTE PROCEDURE modif_amount();
This form only executes the function (modif_amount) if column (net_amount), (iva_amount) has in fact changed value:
CREATE TRIGGER trigger_modif_amount
AFTER UPDATE
ON tb_lines
FOR EACH ROW
WHEN ((OLD.net_amount IS DISTINCT FROM NEW.net_amount) AND
(OLD.iva_amount IS DISTINCT FROM NEW.iva_amount) )
EXECUTE PROCEDURE modif_amount();
You can create trigger which trigger after update of specific fields
https://www.postgresql.org/docs/14/sql-createtrigger.html
CREATE TRIGGER test
AFTER INSERT OR DELETE OR UPDATE OF net_amount, iva_amount
ON tb_lines
FOR EACH ROW
EXECUTE PROCEDURE modif_amount();
Updating overal sum in another table inside trigger is not a good idea (it moves business logic into triggers and is not save).
Version 1
update tb_invoice set
(net_amount,iva_amount,tot_amount) = (select COALESCE(sum(net_amount),0),COALESCE(sum(iva_amount),0),COALESCE(sum(net_amount+iva_amount),0) from tb_lines where invoice_id = coalesce(NEW.invoice_id, OLD.invoice_id))
where invoice_id = coalesce(NEW.invoice_id, OLD.invoice_id);
Version 2
update tb_invoice set
(net_amount,iva_amount,tot_amount) = (select COALESCE(sum(net_amount),0),COALESCE(sum(iva_amount),0),COALESCE(sum(net_amount+iva_amount),0) from tb_lines where tb_lines.invoice_id = tb_invoice.invoice_id)
where invoice_id = coalesce(NEW.invoice_id, OLD.invoice_id);
COALESCE in sum is needed to ensure, that even if invoice has no positions sums will be calculated as 0.
COALESCE in where is needed because during insert operation there is no OLD.invoice_id, and during delete operation there is no NEW.invoice_id.
My assumption is, that invoice_id cannot be changed. If not, that moving a position from one invoice to another should update ole and new one.

SQL constraint: two attributes, at least one foreign key match on same table

I have a table of phone numbers owned by a company, and a table of phone call records. Every call record includes (non-null) source and destination numbers. I am given the integrity constraint that either the source number or the destination number, but not both, are allowed to be numbers that are not in the phone number table (because they are numbers not owned by this company). In other words, I need to ensure that at least one of them is a foreign key to the phone number table.
create table phonenumber (
phonenum numeric(10,0) not null,
primary key (phonenum)
);
create table call_record (
URID varchar(20) not null,
c_src numeric(10,0) not null,
c_dst numeric(10,0) not null,
primary key (URID)
);
The following sounds like what I want but isn't valid SQL:
constraint call_constraint check (
foreign key (c_src) references phonenumber (phonenum) or
foreign key (c_dst) references phonenumber (phonenum)
)
Is there a way to specify this in DDL? If not, how would I write a trigger to enforce this?
Edited:
Here is another idea using DDL and not using trigger:
create table phonenumber (
phonenum numeric(10,0) not null,
primary key (phonenum)
);
Create a function to validate foreign key "by hand".
CREATE OR REPLACE FUNCTION call_check(p_src NUMBER, p_dst NUMBER) RETURN VARCHAR2 DETERMINISTIC IS
BEGIN
FOR x IN (SELECT COUNT(*) c
FROM (SELECT 1
FROM phonenumber
WHERE phonenum = p_src
UNION ALL
SELECT 1
FROM phonenumber
WHERE phonenum = p_dst)) LOOP
IF x.c>=1 AND x.c <= 2 THEN
RETURN 'OK';
END IF;
END LOOP;
RETURN 'NOK';
END;
If you're on 11g and up, then add virtual column and add check on that column
--drop table call_record
create table call_record (
URID varchar(20) not null,
c_src numeric(10,0) not null,
c_dst numeric(10,0) not null,
call_check_col GENERATED ALWAYS AS (call_check(c_src, c_dst)),
primary key (URID)
);
ALTER TABLE call_record ADD CONSTRAINT call_check_con CHECK (call_check_col='OK');
Let's test
SQL> INSERT INTO phonenumber VALUES ('123');
1 row inserted
SQL> INSERT INTO call_record (urid, c_src, c_dst) VALUES ('C1', '123', '321');
1 row inserted
SQL> INSERT INTO call_record (urid, c_src, c_dst) VALUES ('C3', '123', '123');
1 row inserted
SQL> INSERT INTO call_record (urid, c_src, c_dst) VALUES ('C2', '321', '321');
INSERT INTO call_record (urid, c_src, c_dst) VALUES ('C2', '321', '321')
ORA-02290: check constraint (TST.CALL_CHECK_CON) violated

primary key of new row automatically goes to 2nd table where it's a foreign key in a new row

This is my 1st table
CREATE TABLE [dbo].[Booking_Date]
(
[Book_ID] INT IDENTITY (1, 1) NOT NULL,
[Book_Checkin_Date] DATETIME NULL,
[Book_Checkout_date] DATETIME NULL,
[Adults] INT NULL,
[Children] INT NULL,
CONSTRAINT [PK_Booking_Date]
PRIMARY KEY CLUSTERED ([Book_ID] ASC)
);
And this is my 2nd table
CREATE TABLE [dbo].[Room_Detail]
(
[R_D_ID] INT IDENTITY (1, 1) NOT NULL,
[Cust_ID] INT NULL,
[Book_ID] INT NULL,
[Room_ID] INT NULL,
[Room_Price] MONEY NULL,
PRIMARY KEY CLUSTERED ([R_D_ID] ASC),
CONSTRAINT [FK_Room_Detail_Customer]
FOREIGN KEY ([Cust_ID]) REFERENCES [dbo].[Customer] ([Cust_ID]),
CONSTRAINT [FK_Room_Detail_Booking_Date]
FOREIGN KEY ([Book_ID]) REFERENCES [dbo].[Booking_Date] ([Book_ID]),
CONSTRAINT [FK_Room_Detail_Room]
FOREIGN KEY ([Room_ID]) REFERENCES [dbo].[Room] ([Room_ID])
);
When I insert data into my 1st table a with booking date, then primary key of that data automatically is inserted into the 2nd table that is Room Detail in the Book_ID column
If you want to do this in T-SQL / SQL Server, you can use an AFTER INSERT trigger on the Booking_Date table - something like this:
CREATE TRIGGER trgInsertBookingDate
ON dbo.Booking_Date
AFTER INSERT
AS
-- for each row newly inserted into "Booking_Date",
-- insert a new (empty) row into "Room_Detail"
INSERT INTO dbo.Room_Detail(Book_ID)
SELECT i.Book_ID
FROM Inserted i
So every time you insert one or multiple row(s) into Booking_Date, a new (more or less empty) row will be inserted into Room_Detail for each of those new rows inserted. Since you don't have any other information available, you can only set the Book_ID column of Room_Detail in the trigger - the other columns will have to somehow be specified / filled later

CREATE TABLE with column of certain value if other column has a value

Im trying to create table that has two certain columns. In one of them (deliverer) there are a few possible values. In second, there are few possible values as well. Value in second column is required only if deliverer has value 'A', otherwise it can be (and should be in fact a NULL)
this is my atempt
CREATE TABLE dict_shipping_types (
id bigserial NOT NULL,
deliverer varchar(50) NOT NULL,
gage varchar(1)
)
;
ALTER TABLE dict_shipping_types
ADD CONSTRAINT CHK_dict_shipping_types_deliverer CHECK (deliverer IN ('CompanyA','CompanyB','CompanyC'))
;
ALTER TABLE dict_shipping_types
ADD CONSTRAINT CHK_dict_shipping_types_gage_not_null
CHECK (
(DELIVERER='CompanyA' AND GAGE IN ('A','B','C') AND GAGE != NULL ) OR
(DELIVERER!='CompanyA' AND GAGE=NULL)
)
;
ALTER TABLE dict_shipping_types ADD CONSTRAINT PK_dict_shipping_types
PRIMARY KEY (id)
;
Test Queries
INSERT INTO dict_shipping_types(deliverer) VALUES('CompanyA')
INSERT INTO dict_shipping_types(deliverer,GAGE) VALUES('CompanyB','A')
INSERT INTO dict_shipping_types(deliverer,GAGE) VALUES('CompanyA','D')
All of them should fail, but only third one does. How in check i can allow NULL or NOT NULL values?
You can fix it with NOT NULL/IS NOT NULL instead of !=NULL/=NULL
CREATE TABLE dict_shipping_types (
id bigserial NOT NULL,
deliverer varchar(50) NOT NULL,
gage varchar(1)
)
;
ALTER TABLE dict_shipping_types
ADD CONSTRAINT CHK_dict_shipping_types_deliverer CHECK (deliverer IN ('CompanyA','CompanyB','CompanyC'))
;
ALTER TABLE dict_shipping_types
ADD CONSTRAINT CHK_dict_shipping_types_gage_not_null
CHECK (
(DELIVERER='CompanyA' AND GAGE IN ('A','B','C') AND GAGE is not NULL) or
(DELIVERER!='CompanyA' AND GAGE is NULL)
);
ALTER TABLE dict_shipping_types ADD CONSTRAINT PK_dict_shipping_types
PRIMARY KEY (id)
;

Postgres: generate IDs automatically

Objective: Have postgres generate ids automatically
CREATE TABLE user_privilege (
id bigint NOT NULL,
name character varying(255) NOT NULL,
version integer
);
CREATE TABLE
INSERT INTO user_privilege (name, version) values ('XYZ', 1);
ERROR: null value in column "id" violates not-null constraint
ALTER TABLE user_privilege ALTER COLUMN id SET DEFAULT nextval('user_privilege_id_seq'::regclass);
ERROR: relation "user_privilege_id_seq" does not exist
Thanks!
EDIT:
I want to keep my id as bigint as all other tables have id as bigint.
You need to use either SERIAL or BIGSERIAL, not BIGINT.
CREATE TABLE user_privilege (
id BIGSERIAL NOT NULL,
It's not clear whether your table has a PRIMARY KEY or UNIQUE constraint. But it should.
You have to create the sequence at first:
CREATE SEQUENCE user_privilege_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
and after you can use it:
ALTER TABLE ONLY user_privilege ALTER COLUMN id SET DEFAULT nextval('user_privilege_id_seq'::regclass);
Here is the create sequence documentation