Is there inconsistency in PostgreSQL syntax of adding constraints to a table? - sql

From PostgreSQL document
To add a constraint, the table constraint syntax is used. For example:
ALTER TABLE products ADD CHECK (name <> '');
ALTER TABLE products ADD CONSTRAINT some_name UNIQUE (product_no);
ALTER TABLE products ADD FOREIGN KEY (product_group_id) REFERENCES product_groups;
To add a not-null constraint, which cannot be written as a table
constraint, use this syntax:
ALTER TABLE products ALTER COLUMN product_no SET NOT NULL;
Why the word after ADD differ for constraints of different kinds?
Why does unique constraint has a more generic ADD CONSTRAINT some_name than check and foreign key?
Why is not null constraint specified by ALTER COLUMN, instead of ADD CONSTRAINT some_name NOT NULL (col_name)?
Is there inconsistency in PostgreSQL syntax of adding constraints to a table?
Does this belong to the SQL standard?

The word after ADD differs so the database knows what you mean. E.g. CHECK introduces a generic boolean condition; UNIQUE is followed by a list of column names; FOREIGN KEY is followed by a column name, REFERENCES, and a target table/column. Without these keywords it would be ambiguous which kind of constraint you mean.
The CONSTRAINT constraint_name syntax is not limited to unique constraints. See the definition of column_constraint and table_constraint in https://www.postgresql.org/docs/10/static/sql-createtable.html; both allow an optional leading CONSTRAINT constraint_name to name the constraint.
As for NOT NULL, see https://www.postgresql.org/docs/10/static/ddl-constraints.html#id-1.5.4.5.6:
A not-null constraint is always written as a column constraint. A not-null constraint is functionally equivalent to creating a check constraint CHECK (column_name IS NOT NULL), but in PostgreSQL creating an explicit not-null constraint is more efficient. The drawback is that you cannot give explicit names to not-null constraints created this way.
I assume not-null constraints are a special case internally, allowing for better optimization than a generic CHECK constraint, which can use any boolean expression.

We cant use add constraint syntax for Not Null. You have to use modify column syntax to add not null
eg.
alter table modify ( not null);

Related

Unclear constraints in SQL's CREATE TABLE

In a Coursera course, there is a snippet of code:
I don't understand the parts:
CONSTRAINT AUTHOR_PK
(author_id) (after PRIMARY KEY)
Could you please explain?
Clarifications: for CONSTRAINT AUTHOR_PK, I don't understand why CONSTRAINT is there explicitly but it's not there for the other attributes of the table. I also don't know what AUTHOR_PK is used for.
For (author_id), I don't understand its presence. Since PRIMARY KEY is written on the same line as author_id, isn't it already implicit that author_id will be used as the primary key?
I'm very new to SQL. I consulted
https://www.w3schools.com/sql/sql_create_table.asp
https://www.w3schools.com/sql/sql_constraints.asp
but could not resolve these issues myself.
There are two types of constraints you can create in a CREATE TABLE statement. These are column constraints and table constraints.
Column constraints are included in the definition of a single column.
Table constraints are included as separate declarations, not part of a column definition.
This is the same table with the primary key declared as a table constraint:
CREATE TABLE Author
(author_id CHAR(2),
lastname VARCHAR(15) not null,
...,
CONSTRAINT PK_AUTHOR PRIMARY KEY (author_id)
)
What you have in your example is a constraint being declared as a column constraint. Normally, column constraints don't have to name which columns they're relevant to since they're part of the column's definition, and indeed in some dialects of SQL, the sample you've shown would be rejected, because it does name the columns explicitly.
PK_AUTHOR, in both your sample and mine, is being used to give a specific name to the constraint. This is helpful if you'll later need to remove the constraint. If you don't want to name the constraint, then CONSTRAINT PK_AUTHOR may be omitted from either sampe.
The CONSTRAINT keyword is necessary when you want to provide a specific name for the constraint, in this case AUTHOR_PK. If you don't do this, a name would be auto-generated, and these names are generally not very useful. All the NOT NULL constraints in this example would have auto-generated names.
In my experience, it's standard practice to name all constraints except NOT NULL ones.
I think you are right that (author_id) is unnecessary in this example, as it is implied by the fact that the constraint is declared for that column already. But the syntax allows it. (I wonder if it would allow specifying a different column in this position - I don't think so but haven't tried it.)
The syntax to specify columns is more useful when you want to declare a multiple-column key. In this case, the CONSTRAINT clause would be specified as if it were another column in the table definition:
...
country CHAR(2),
CONSTRAINT one_city_per_country UNIQUE (country,city)
);

How to add check constraint on a column in table?

ALTER TABLE Student ADD CONSTRAINT CHK_Student_ID_LENGTH CHECK (LEN([ID]) between 12 and 14);
SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_NAME = 'CHK_Student_ID_LENGTH';
alter table Student drop constraint CHK_Student_ID_LENGTH;
I tried to add check constraint using the first statement. It gave me the below error.
[23000][547] The ALTER TABLE statement conflicted with the CHECK constraint "CHK_Student_ID_LENGTH". The conflict occurred in database, table "Student", column 'ID'.
I tried to check if there was any existing constraint with the same name. But I did not get any. Still I tried to drop the constraint. But then it gave the error:
CHK_Student_ID_LENGTH is not a constraint.
But still add constraint statement gives error saying it already exists. Where am I going wrong?
The error message is a little badly worded, but isn't saying what you think it's saying.
It's not saying that there's already a constraint with the same name. It's saying that the constraint is being violated. That means that there is data already in the table that doesn't meet the requirements of the new constraint you're trying to introduce.
You could use the NOCHECK option to create the constraint whilst allowing existing data to violate it. But this is frequently the wrong thing to do. It is usually more sensible to fix the existing data.
Specifying NOCHECK means that the constraint can't be used by the optimizer to eliminate redundant actions that the logic of the constraint would preclude.
You probably have some records in Your table that conflict with that new constraint.
Just find them and UPDATE/DELETE before adding a contraint.
SELECT *
FROM Student
WHERE LEN([ID]) between 12 and 14
Or try to add that constraint without checking existing values using WITH NOCHECK
ALTER TABLE Student WITH NOCHECK
ADD CONSTRAINT CHK_Student_ID_LENGTH CHECK (LEN([ID]) between 12 and 14);
Tip: In migration files I usually add this before adding a new constraint:
IF OBJECT_ID('CHK_Student_ID_LENGTH') IS NOT NULL
ALTER TABLE Student DROP CONSTRAINT CHK_Student_ID_LENGTH
IF OBJECT_ID('CHK_Student_ID_LENGTH') IS NULL
ALTER TABLE Student ADD CONSTRAINT ...
More about ALTER TABLE https://learn.microsoft.com/en-us/sql/t-sql/statements/alter-table-transact-sql?view=sql-server-ver15

Naming a default constraint

I'm trying to create a default constraint here, but the system is generating a weird name for it. If I want to name it df_MY_TABLE_GUID or something, how could I specify that name be used?
ALTER TABLE MY_TABLE
ADD MY_GUID uniqueidentifier NOT NULL
CONSTRAINT uq_MY_TABLE_GUID UNIQUE (MY_TABLE_GUID)
DEFAULT NEWID() WITH VALUES
Just specify the constraint name with the full syntax, like the UNIQUE in your example:
ALTER TABLE MY_TABLE ADD MY_GUID UNIQUEIDENTIFIER NOT NULL
CONSTRAINT uq_MY_TABLE_GUID UNIQUE (MY_TABLE_GUID)
CONSTRAINT df_MY_TABLE_GUID DEFAULT NEWID() WITH VALUES ;
As a matter of routine, I always prefer and encourage to always name every single constraint I create, for the sake of easy reference latter on.

POSTGRES - Handling several ON CONFLICT constraints/indexes

Have the following in the table where:
CONSTRAINT unique_position UNIQUE (id,city,type)
and
CREATE UNIQUE INDEX unique_position_sat_null ON public."position" (id,city) where type is null
on insert conflict there is currently:
ON CONFLICT ON CONSTRAINT unique_position DO UPDATE SET
.....
How can I incorporate both unique_position and unique_position_sat_null into ON CONFLICT ON CONSTRAINT
Tried:
ON CONFLICT ON CONSTRAINT unique_position DO UPDATE SET
.....,
ON CONFLICT ON CONSTRAINT unique_position_sat_null DO UPDATE SET
.....,
and
ON CONFLICT ON CONSTRAINT unique_position and unique_position_sat_null DO UPDATE SET
.....,
Thank you.
Unfortunately, unique indexes and unique constraints are not the same things in Postgres.
When you define a unique index, not constraint, Postgres doesn't create a constraint with the same name (in case of collisions during INSERTs it will produce error "unique constraint violation" mentioning index' name, but this is a minor bug, see https://www.postgresql.org/message-id/flat/CAH2-Wzn-uXcLgC5uFbqe2rUfmJWP9AxKnMKAEgqU26hbURxk5A%40mail.gmail.com#CAH2-Wzn-uXcLgC5uFbqe2rUfmJWP9AxKnMKAEgqU26hbURxk5A#mail.gmail.com)
So you cannot use index name like it is a constraint name.
You could do ALTER TABLE .. ADD CONSTRAINT .. UNIQUE, but it cannot be unique.
So this can be considered as a not yet implemented feature in Postgres -- either "ON CONFLICT" clause needs to learn how to use index names or ALTER TABLE .. ADD CONSTRAINT .. UNIQUE needs to be patched to allow partial constraints.
I suggest discussing it in pgsql-hackers# mailing list (e.g. if you answer thread mentioned above, explaining your problem, it would be awesome).

Oracle SQL Foreign Key constraint that ignores null or historic values

I am trying to add a constrain to a database table that I want to modify. I want to add a constraint on a column so it references a primary key of another tables. Easy enough, I just have to add a foreign key constraint. The problem is that the column already has some values that are null or something that is not part of the table I will be referencing.
My question is how do I add a constraint that references a primary key but can also accept null values (the primary key always has a value) and how to ignore the existing values so far. Is it possible? If the second part is not, I am thinking I could always write a script that updates all the nonsense values (they have a format of sort if that I can reg ex) to null so they only thing I have to figure out is how to add a foreign key constraind that also accepts null values
Firstly, there's nothing stopping you from adding a referential constraint on a column that has NULLs - foreign key constraints are only enforced for non-NULL values.
Secondly, if there are existing values that do not exist in the parent table, and you can't fix them, you do have the option in Oracle to make the constraint only validated for newly inserted or updated rows, using the NOVALIDATE option, e.g.
ALTER TABLE x ADD CONSTRAINT fk FOREIGN KEY (id) REFERENCES parent (id) NOVALIDATE;
The only downside to using the NOVALIDATE option is that the query optimizer will not rely on the FK constraint, and will execute queries with the assumption that there may be rows that do not have a matching parent row.
It would be a good idea if you are able to fix the missing values, afterwards to alter the constraint to VALIDATE.
Primary keys can not contain NULL values in all databases (proof)
To add a foreign key you should do something like:
ALTER TABLE table1
ADD CONSTRAINT fk_table1_2
FOREIGN KEY (column1)
REFERENCES table2(column2);