Create a unique constraint with CHECK in PostgreSQL - sql

Consider that there is the following table:
create table table_1(
id serial
PRIMARY KEY,
col1 varchar(50),
col2 varchar(50),
status varchar(1) -- A=active P=pending D=Deleted
);
now what I want is to create a unique constraint on (col1,col2) but it should not consider those with status ='D' i.e Consider there is 2 rows in the table:
INSERT INTO table_1(col1,col2,status) VALUES ('row1', 'row1', 'A');
INSERT INTO table_1(col1,col2,status) VALUES ('row2', 'row2', 'D');
Then I should NOT be able to add the following row:
INSERT INTO table_1(col1,col2,status) VALUES ('row1', 'row1', 'A');
But I SHOULD be able to add the following row:
INSERT INTO table_1(col1,col2,status) VALUES ('row2', 'row2', 'A');

You can do this with a partial unique index:
create unique index id_table1_col1_col2_status on table_1(col1, col2, status)
where status <> 'D';
Here is a SQL Fiddle that you can use to see it work.

Related

SQL table with primary key over two columns and unique values in both columns

How to create a table in SQL with the following attributes?
The table has two columns A and B.
The primary key of the table is (A, B).
All values in A are unique. Pseudo code: (Count(A) == COUNT(SELECT DISTINCT A)).
All values in B are also unique.
CREATE TABLE IF NOT EXISTS myTable(
A VARCHAR(32) PRIMARY KEY NOT NULL, -- A HAS DISTINCT VALUES
B VARCHAR(32) NOT NULL -- B HAS DISTINCT VALUES
);
INSERT INTO myTable VALUES ('A1', 'B1') --> Add value
INSERT INTO myTable VALUES ('A1', 'B2') --> Do not add value
INSERT INTO myTable VALUES ('A2', 'B2') --> Add value
INSERT INTO myTable VALUES ('A3', 'B3') --> Add value
INSERT INTO myTable VALUES ('A4', 'B3') --> Do not add value
INSERT INTO myTable VALUES ('A4', 'B4') --> Add value
INSERT INTO myTable VALUES ('A5', 'B6') --> Add value
To define a compound PRIMARY KEY:
CREATE TABLE myTable
(
A VARCHAR(32) NOT NULL,
B VARCHAR(32) NOT NULL,
CONSTRAINT PK_AB primary key (A,B),
CONSTRAINT UQ_A UNIQUE(A),
CONSTRAINT UQ_B UNIQUE(B)
);
Please note: a table with just 2 columns with both columns in the primary key smells funny.

Update multiple tables in trigger where one of the tables is used for trigger activation

Let's say I have two tables called widgetCustomer and widgetSale. On an insert in widgetSale I want to add a timestamp to the widgetSale row and add the sale id as last_order_id to the widgetCustomer table.
I understand using AFTER INSERT ON will result in an error on trying to update the NEW row, hence we need to use BEFORE INSERT ON clause. Which brings forward a new issue that AUTO_INCREMENT has not yet generated a id for sale hence last_order_id would all be zero. There is a method to do this at MySQL/MariaDB TRIGGER but it seems to fail on my system (i.e., the last order ids are still zero).
As a work around I'm using two different triggers one before insert and one after insert. Although it does work I'm keen to learn if there is a possible flaws with the method above and is there a better way of doing this (both in terms of performance and data integrity).
My code is given below:
DROP TABLE IF EXISTS widgetSale;
DROP TABLE IF EXISTS widgetCustomer;
DROP TABLE IF EXISTS widgetLog;
CREATE TABLE widgetCustomer ( id integer primary key AUTO_INCREMENT, name TEXT, last_order_id INT, stamp TEXT );
CREATE TABLE widgetSale ( id integer primary key AUTO_INCREMENT, item_id INT, customer_id INTEGER, quan INT, price INT, stamp TEXT );
CREATE TABLE widgetLog ( id integer primary key AUTO_INCREMENT, stamp TEXT, event TEXT, username TEXT, tablename TEXT, table_id INT);
INSERT INTO widgetCustomer (name) VALUES ('Bob');
INSERT INTO widgetCustomer (name) VALUES ('Sally');
INSERT INTO widgetCustomer (name) VALUES ('Fred');
SELECT * FROM widgetCustomer;
CREATE TRIGGER stampSale BEFORE INSERT ON widgetSale
FOR EACH ROW BEGIN
SET NEW.stamp = CURRENT_TIMESTAMP();
END
CREATE TRIGGER stampOnRest AFTER INSERT ON widgetSale
FOR EACH ROW BEGIN
UPDATE widgetCustomer SET last_order_id = NEW.id, stamp = CURRENT_TIMESTAMP()
WHERE widgetCustomer.id = NEW.customer_id;
INSERT INTO widgetLog (stamp, event, username, tablename, table_id)
VALUES (CURRENT_TIMESTAMP(), 'INSERT', 'TRIGGER', 'widgetSale', NEW.id);
END
INSERT INTO widgetSale (item_id, customer_id, quan, price) VALUES (1, 3, 5, 1995);
INSERT INTO widgetSale (item_id, customer_id, quan, price) VALUES (2, 2, 3, 1495);
INSERT INTO widgetSale (item_id, customer_id, quan, price) VALUES (3, 1, 1, 2995);
SELECT * FROM widgetSale;
SELECT * FROM widgetCustomer;
SELECT * FROM widgetLog;
I'm using mariadb 10.6.* on Archlinux.

INSERT fails with ORA-01400: cannot insert NULL

I am working with ORACLE SQL Developer and I created a table with an id as PK, another FK, id_columnx and a column1 and inserted data into them. Then I added another 2 columns, column 2 and column 3 and when I try to insert data into these new added columns, I get the error:
ORA-01400: cannot insert NULL.
I have to mention that I don't have any triggers on the table and i DO have values in the INSERT statement. There seems to be a conflict with the PK id, but I don't understand why.
So here is the code:
create table mytable(id INT PRIMARY KEY, name varchar2(30));
insert into mytable values (1, 'Mary');
insert into mytable values (2, 'John');
insert into mytable values (3, 'Bill');
alter table mytable
add email VARCHAR2(30);
alter table mytable
add addess VARCHAR2(30);
insert into mytable (email, addess)
values ('mary#gmail.com', 'Street X');
And here is the error I get:
Error starting at line : 12 in command -
insert into mytable (email, addess)
values ('mary#gmail.com', 'Street X')
Error report -
ORA-01400: cannot insert NULL into ("ZAMFIRESCUA_49"."MYTABLE"."ID")
INSERT is for inserting new rows, UPDATE is for altering data in the current rows. As mentioned in the comments, it looks like you want to be updating Mary's row with her email/address:
UPDATE mytable
SET email = 'mary#gmail.com',
address = 'Street X'
WHERE ID = 1 --Mary's ID for example, replace with the ID of the row you want to update
You could also use a subquery to find the right ID so you don't have to always look it up:
UPDATE mytable
SET email = 'mary#gmail.com',
address = 'Street X'
WHERE ID = (SELECT ID FROM mytable WHERE name = 'Mary')
Edit:
I was thinking there were two tables while writing this answer, you could always just use the name field as your filter:
UPDATE mytable
SET email = 'mary#gmail.com',
address = 'Street X'
WHERE name = 'Mary'
You're missing a PK value in your last INSERT after the ALTER statements. Try this:
create table mytable(id INT PRIMARY KEY, name varchar2(30));
insert into mytable values (1, 'Mary');
insert into mytable values (2, 'John');
insert into mytable values (3, 'Bill');
alter table mytable add email VARCHAR2(30);
alter table mytable add addess VARCHAR2(30);
insert into mytable (id, email, addess) values (4, 'mary#gmail.com', 'Street X');

PostgreSQL connections constraint

I have table with two columns, id1 and id2.
If i have for example foo-bar respectively in these columns,I need a constraint that forbids entry bar-foo.
Thanks!
CREATE TABLE mytable(
id1 integer,
id2 integer
);
CREATE UNIQUE INDEX ON mytable(least(id1, id2), greatest(id1, id2));
This should ddo the trick:
test=> INSERT INTO mytable VALUES (1, 2);
INSERT 0 1
test=> INSERT INTO mytable VALUES (1, 3);
INSERT 0 1
test=> INSERT INTO mytable VALUES (2, 1);
ERROR: duplicate key value violates unique constraint "mytable_least_greatest_idx"
DETAIL: Key ((LEAST(id1, id2)), (GREATEST(id1, id2)))=(1, 2) already exists.

SQL Error: ORA-00933: SQL command not properly ended Error at Line: 3 Column: 35

I'm creating a script that creates a TABLE and I have problem with the last command which I have to do:
SELECT Studenci.Nazwisko,
Trunc(Months_Between(Sysdate,Studenci.RokUrodzenia)/12) Wiek FROM
Studenci.RokUrodzenia to_date('1980','YYYY')
This is my script; what am I doing wrong?
CREATE TABLE Studenci(
NrIndeksu NUMBER(3) PRIMARY KEY,
Nazwisko VARCHAR2(16),
RokUrodzenia NUMBER(4),
Kierunek VARCHAR2(12)
);
CREATE TABLE Wykladowcy(
IdWykladowcy NUMBER(4) PRIMARY KEY,
Nazwisko VARCHAR2(16),
Stopien VARCHAR2(6),
Stanowisko VARCHAR(8)
);
CREATE TABLE Kursy(
IdKursu NUMBER(1) PRIMARY KEY,
Nazwa VARCHAR2(18),
IdWykladowcy NUMBER(4) REFERENCES Wykladowcy
);
CREATE TABLE Rejstracje(
NrIndeksu NUMBER(3) REFERENCES Studenci ,
IdKursu NUMBER(1) REFERENCES Kursy ,
Data DATE
);
CREATE UNIQUE INDEX Ind_Kursy_naz ON Kursy(Nazwa);
CREATE INDEX Ind_Studenci_naz ON Studenci(Nazwisko);
CREATE INDEX Ind_Wykladowcy_naz ON Wykladowcy(Nazwisko);
INSERT INTO Wykladowcy VALUES (1010,'Kowalski Jan', 'Dr', 'Adiunkt');
INSERT INTO Wykladowcy VALUES (1011,'Jakubowski Emil','Dr hab','Docent');
INSERT INTO Wykladowcy VALUES (1012,'Gazda Mirosław','Dr','Profesor');
INSERT INTO Kursy VALUES (1,'Bazy danych',1010);
INSERT INTO Kursy VALUES (2,'Systemy operacyjne',1012);
INSERT INTO Kursy VALUES (3,'Multimedia',1011);
INSERT INTO Kursy VALUES (4,'Sieci komputerowe',null);
INSERT INTO Studenci VALUES (101,'Kuczyńska Ewa',1980,'Bazy danych');
INSERT INTO Studenci VALUES (102,'Lubicz Robert',1985,'Multimedia');
INSERT INTO Studenci VALUES (103,'Krajewski Bogdan',1988,'Bazy danych');
INSERT INTO Studenci VALUES (104,'Lityńska Anna',1987,'Multimedia');
INSERT INTO Studenci VALUES (105,'Marzec Marcin',1982,'Multimedia');
INSERT INTO Studenci VALUES (106,'Cichaocki Rafał',1989,'Bazy danych');
INSERT INTO Rejstracje VALUES (101,1,NULL);
INSERT INTO Rejstracje VALUES (102,3,NULL);
INSERT INTO Rejstracje VALUES (104,3,NULL);
INSERT INTO Rejstracje VALUES (106,1,NULL);
INSERT INTO Rejstracje VALUES (104,2,NULL);
INSERT INTO Rejstracje VALUES (101,4,NULL);
INSERT INTO Rejstracje VALUES (103,1,NULL);
INSERT INTO Rejstracje VALUES (103,1,NULL);
INSERT INTO Rejstracje VALUES (105,1,NULL);
UPDATE Rejstracje SET Rejstracje.IdKursu=Rejstracje.IdKursu*3
WHERE Rejstracje.NrIndeksu=105;
COMMIT UPDATE
INSERT INTO Rejstracje(Data)
VALUES (Sysdate);
SELECT *FROM Kursy
WHERE Kursy.IdWykladowcy IS NULL;
SELECT *FROM Rejstracje
WHERE Rejstracje.NrIndeksu=101;
SELECT Kursy.Nazwa
FROM Kursy
ORDER BY Nazwa ASC;
SELECT Studenci.Nazwisko,
Trunc(Months_Between(Sysdate,Studenci.RokUrodzenia)/12) Wiek
FROM Studenci.RokUrodzenia to_date('1980','YYYY')
From the error message it seems like a missing ;.
This SQL:
SELECT Studenci.Nazwisko,
Trunc(Months_Between(Sysdate,Studenci.RokUrodzenia)/12) Wiek
FROM Studenci.RokUrodzenia to_date('1980','YYYY');
is invalid because the to_date() call can't go there like that.
You are probably looking for something like:
SELECT Studenci.Nazwisko,
Trunc(Months_Between(Sysdate,Studenci.RokUrodzenia)/12) Wiek
FROM Studenci
WHERE RokUrodzenia = 1980;
given that RokUrodenzia is a NUMBER(4) column and not a DATE column. You may still have to do some work to get the MONTHS_BETWEEN() call to work since RokUrodenzia is not a DATE (as I said) and MONTHS_BETWEEN probably expects a date. You'll have to investigate how to fix that; you might use TO_DATE() there legitimately.