T-SQL: Add NOT NULL constraint to multiple existing columns at once - tsql

I have a table like below:
CREATE TABLE dbo.MyTable (
Text int,
column2 varchar(50),
column3 varchar(50)
)
and now I would like using T-SQL to apply a NOT NULL constraints to the three columns at once (Note that I have a column called Text that matches with a reserved type in T-SQL), so I perform:
ALTER TABLE dbo.MyTable
ADD CONSTRAINT NOT_NULL NOT NULL (Text, column2, column3)
but it is not working:
Incorrect syntax near reserved word 'not'.

For one, your syntax is wrong.
ALTER TABLE dbo.MyTable
ALTER COLUMN column2 VARCHAR(50) NOT NULL
Note that NOT NULL doesn't quite behave as a constraint, but rather a feature of the column's type. As such, you don't use the ADD CONSTRAINT operation.
Secondly, you just have to use multiple statements, one for each column. There's really no benefit to adding the constraints all at once, so it doesn't matter.

The NOT NULL thing is not a constraint, but is part of the data type of the column, so the ADD CONSTRAINT don't applies at all to begin with. The correct way to do is to use an ALTER COLUMN, so the correct syntax would be
ALTER TABLE dbo.MyTable ALTER COLUMN [Text] INT NOT NULL,
[column2] VARCHAR(50) NOT NULL,
[column3] VARCHAR(50) NOT NULL ;
The thing of the column name colliding with the reserved keyword is deal with by enclosing the name in [], but that's not the original problem in your example.

Related

Add a constraint with a where statement

I want to add a NOT NULL to a column that is conditional on another column.
ALTER TABLE mailers ALTER COLUMN original_id SET NOT NULL
WHERE offer_type = 'client cross'
In other words, if one column have 'client cross' do not let a different column be NULL.
You cannot create a NOT NULL column constraint with a condition. Either the column can contain NULL values or it can't.
However, you can create a normal CHECK constraint for the entire row:
ALTER TABLE mailers
ADD CONSTRAINT "non-null_original_id_for_client-cross"
CHECK (original_id IS NOT NULL OR offer_type <> 'client cross');

No overlapping data

Being a new user of postgres, I have created a database in postgres 13. It contains tables
including 4 Fields
ID integer (PK)
HoleID varchar(20)
From numeric NOT NULL CHECK (From>=0)
To numeric
Cat varchar (20)
I want to create a constraint that will check that for an identical entity number, the From and To fields of a record must not overlap with another record.
I have proposed the exclude constraint below but it does not work
ADD CONSTRAINT no_overlap EXCLUDE USING GIST ("HoleID" WITH =, ("mFrom", "mTo") WITH &&);
Thank you for helping me.
This is easier with a single numrange column instead of from/to.
create table thing (
id bigserial primary key,
holeid varchar(20),
range numrange not null,
exclude using gist (range with &&, holeid with =)
);
insert into thing (holeid, range) values
('foo', '[1, 10]'::numrange),
-- ok, same holeid, does not overlap
('foo', '[10.1, 11]'::numrange),
-- ok, different holeid, overlaps.
('bar', '[2,5]'::numrange),
-- not ok, same holeid, overlaps.
('foo', '[0, 1]'::numrange);
Demonstration.
Note that [] is inclusive and () is exclusive. (1,10)::numrange and (10,20)::numrange do not overlap. [1,10]::numrange and [10,20]::numrange do overlap.
You can create the constraint like this:
ALTER TABLE tab
ADD CONSTRAINT no_overlap EXCLUDE USING gist (
"HoleID" WITH =,
numrange("mFrom", "mTo") WITH &&
);
You should never store a timestamp as number.

Add a not null constraint to a table in which a column has a certain value in PostgreSQL

I'm trying to add a NOT NULL constraint in PostgreSQL, but can't find the right syntax.
Here is, essentially, what I'm trying to do:
ALTER TABLE olimpic.tb_athlete
ADD CONSTRAINT soloESP CHECK(country = 'ESP' AND substitute_id IS NOT NULL)
and the table I'm trying to modify:
CREATE TABLE olimpic.tb_athlete
(
athlete_id CHAR(7) PRIMARY KEY,
name VARCHAR(50) NOT NULL,
country CHAR(3) NOT NULL,
substitute_id CHAR(7),
FOREIGN KEY (substitute_id) REFERENCES olimpic.tb_athlete(athlete_id)
);
I have already deleted or set default values to the column country where the value is 'ESP', with this code being an example:
DELETE FROM olimpic.tb_athlete
where substitute_id is NULL and country = 'ESP';
but I'm still getting the following error:
ERROR: ERROR: check constraint 'soloESP' on table tb_athlete is violated by some row
SQL state: 23514
Any help you could give me as to how to proceed would be greatly appreciated.
Do you realize that the constraint you're trying to add does not allow any rows with the country field other than 'ESP'? You say you want two conditions simultaneously (because you use the AND operator): each row must have country = 'ESP' AND non-null substitute_id
I believe what you wanted is
ALTER TABLE olimpic.tb_athlete
ADD CONSTRAINT soloESP CHECK(country != 'ESP' OR substitute_id IS NOT NULL);
This constraint will ensure that if country = 'ESP' then substitute_id must be non-null. For other countries both null and non-null values of substitute_id are valid.
But the above is only a guess because you provided neither your database's schema, nor meanings of the fields, nor the error text in English, nor the data stored in your database so that we could analyze what is really happening in your case. Please, consider editing the question to add the above

What is the best way to work with an optional FK constraint in Postgres? [duplicate]

This question already has answers here:
How can you represent inheritance in a database?
(7 answers)
Closed 3 years ago.
I have a table in Postgres which contains Things. Each of these Things can be of 1 of 3 types:
is a self contained Thing
is an instance of a SuperThingA
is an instance of a SuperThingB.
Essentially, in object terms, there's an AbstractThing, extended in
different ways by SuperThingA or SuperThingB and all the records on
the Thing table are either unextended or extended in 1 of the 2
ways.
In order to represent this on the Thing table, I have a number of fields which are common to Things of all types, and I have 2 optional FK reference columns into the SuperThingA and SuperThingB tables.
Ideally, I would like a "FK IF NOT NULL" constraint on each the two, but this doesn't appear to be possible; as far as I can see, all I can do is make both fields nullable and rely on the using code to maintain the FK relationships, which is pretty sucky. This seems to be doable in other databases, for example SQL Server, as per SQL Server 2005: Nullable Foreign Key Constraint, but not any way that I've found so far in PG
How else can I handle this - an insert/update trigger which checks the values when either of those fields is not null and checks the value is present on whichever parent table? That's maybe doable on a small parent table with limited inserts on the Thing table (which, in fairness, is largely the case here - no more than a couple of hundred records in each of the parent tables, and small numbers of inserts on the Thing table), but in a more general case, would be a performance black hole on the inserts if one or both parent table were large
This is not currently enforced with a FK relationship. I've reviewed the PG docs, and it seem pretty definitive that I can't have an optional FK relatioship (which is understandable). It leaves me with a table definition something like this:
CREATE TABLE IF NOT EXISTS Thing(
Thing_id int4 NOT NULL,
Thing_description varchar(40),
Thing_SuperA_FK int4,
Thing_SuperB_FK char(10),
CONSTRAINT ThingPK PRIMARY KEY (Thing_id)
)
;
Every foreign key on a nullable column is only enforced when the value is non-null. This is default behavior.
create table a (
id int4 not null primary key
);
create table b (
id int4 not null primary key,
a_id int4 references a(id)
);
In the example table b has an optional reference to table a.
insert into a values(1); -- entry in table a
insert into b values (1, 1); -- entry in b with reference to a
insert into b values (2, null); -- entry in b with no reference to a
Depending on your use case it also might make sense to reverse your table structure. Instead of having the common table pointing to two more specialized tables you can have it the other way around. This way you avoid the non-null columns entirely.
create table common(
id int4 primary key
-- all the common fields
);
create table special1(
common_id int4 not null references common(id)
-- all the special fields of type special1
);
create table special2(
common_id int4 not null references common(id)
-- all the special fields of type special2
);
You need your SuperN_FK fields defined as nullable foreign keys, then you'll need check constraint(s) on the table to enforce the optional NULLability requirements.
CREATE TABLE Things
( ID int primary key
, col1 varchar(1)
, col2 varchar(1)
, SuperA_FK int constraint fk_SuperA references Things(ID)
, cola1 varchar(1)
, constraint is_SuperA check ((SuperA_FK is null and cola1 is null) or
(SuperA_FK is not null and cola1 is not null))
, SuperB_FK int constraint fk_SuperB references Things(ID)
, colb1 varchar(1)
, constraint is_SuberB check ((SuperB_FK is null and colb1 is null) or
(SuperB_FK is not null))
, constraint Super_Constraint check (
case when SuperA_FK is not null then 1 else 0 end +
case when SuperB_FK is not null then 1 else 0 end
<= 1 )
);
In the above example I've split the check constraints up for ease maintenance. The two is_SuperN constraints enforce the NULL requirements on the FK and it's related detail columns, either all NULLs or the FK is not null and some or all of the detail columns are not null. The final Super_Constraint ensures that at most one SuperN_FK is not null.

Alter column set not null fails

Consider the following table with approximately 10M rows
CREATE TABLE user
(
id bigint NOT NULL,
...
CONSTRAINT user_pk PRIMARY KEY (id)
)
WITH (
OIDS=FALSE
)
Then i applied the following alter
ALTER TABLE USER ADD COLUMN BUSINESS_ID VARCHAR2(50);
--OK
UPDATE USER SET BUSINESS_ID = ID; //~1500 sec
--OK
ALTER TABLE USER ALTER COLUMN BUSINESS_ID SET NOT NULL;
ERROR: column "business_id" contains null values
SQL state: 23502
This is very strange since id column (which has been copied to business_id column) can't contain null values since it is the primary key, but to be sure i check it
select count(*) from USER where BUSINESS_ID is null
--0 records
I suspect that this is a bug, just wondering if i am missing something trivial
The only logical explanation would be a concurrent INSERT.
(Using tbl instead of the reserved word user as table name.)
ALTER TABLE tbl ADD COLUMN BUSINESS_ID VARCHAR2(50);
--OK
UPDATE tbl SET BUSINESS_ID = ID; //~1500 sec
--OK
-- concurrent INSERT HERE !!!
ALTER TABLE tbl ALTER COLUMN BUSINESS_ID SET NOT NULL;</code></pre>
To prevent this, use instead:
ALTER TABLE tbl
ADD COLUMN BUSINESS_ID VARCHAR(50) DEFAULT ''; -- or whatever is appropriate
...
You may end up with a default value in some rows. You might want to check.
Or run everything as transaction block:
BEGIN;
-- LOCK tbl; -- not needed
ALTER ...
UPDATE ...
ALTER ...
COMMIT;
You might take an exclusive lock to be sure, but ALTER TABLE .. ADD COLUMN takes an ACCESS EXCLUSIVE lock anyway. (Which is only released at the end of the transaction, like all locks.)
Maybe it wants a default value? Postgresql docs on ALTER:
To add a column, use a command like this:
ALTER TABLE products ADD COLUMN description text;
The new column is initially filled with whatever default value is given (null if you don't specify a DEFAULT clause).
So,
ALTER TABLE USER ALTER COLUMN BUSINESS_ID SET DEFAULT="",
ALTER COLUMN BUSINESS_ID SET NOT NULL;
You cannot do that at the same transaction. Add your column and update it. Then in a separate transaction set the not null constraint.