WITH CHECK ADD CONSTRAINT followed by CHECK CONSTRAINT vs. ADD CONSTRAINT - sql

I'm looking at the AdventureWorks sample database for SQL Server 2008, and I see in their creation scripts that they tend to use the following:
ALTER TABLE [Production].[ProductCostHistory] WITH CHECK ADD
CONSTRAINT [FK_ProductCostHistory_Product_ProductID] FOREIGN KEY([ProductID])
REFERENCES [Production].[Product] ([ProductID])
GO
followed immediately by :
ALTER TABLE [Production].[ProductCostHistory] CHECK CONSTRAINT
[FK_ProductCostHistory_Product_ProductID]
GO
I see this for foreign keys (as here), unique constraints and regular CHECK constraints; DEFAULT constraints use the regular format I am more familiar with such as:
ALTER TABLE [Production].[ProductCostHistory] ADD CONSTRAINT
[DF_ProductCostHistory_ModifiedDate] DEFAULT (getdate()) FOR [ModifiedDate]
GO
What is the difference, if any, between doing it the first way versus the second?

The first syntax is redundant - the WITH CHECK is default for new constraints, and the constraint is turned on by default as well.
This syntax is generated by the SQL management studio when generating sql scripts -- I'm assuming it's some sort of extra redundancy, possibly to ensure the constraint is enabled even if the default constraint behavior for a table is changed.

To demonstrate how this works--
CREATE TABLE T1 (ID INT NOT NULL, SomeVal CHAR(1));
ALTER TABLE T1 ADD CONSTRAINT [PK_ID] PRIMARY KEY CLUSTERED (ID);
CREATE TABLE T2 (FKID INT, SomeOtherVal CHAR(2));
INSERT T1 (ID, SomeVal) SELECT 1, 'A';
INSERT T1 (ID, SomeVal) SELECT 2, 'B';
INSERT T2 (FKID, SomeOtherVal) SELECT 1, 'A1';
INSERT T2 (FKID, SomeOtherVal) SELECT 1, 'A2';
INSERT T2 (FKID, SomeOtherVal) SELECT 2, 'B1';
INSERT T2 (FKID, SomeOtherVal) SELECT 2, 'B2';
INSERT T2 (FKID, SomeOtherVal) SELECT 3, 'C1'; --orphan
INSERT T2 (FKID, SomeOtherVal) SELECT 3, 'C2'; --orphan
--Add the FK CONSTRAINT will fail because of existing orphaned records
ALTER TABLE T2 ADD CONSTRAINT FK_T2_T1 FOREIGN KEY (FKID) REFERENCES T1 (ID); --fails
--Same as ADD above, but explicitly states the intent to CHECK the FK values before creating the CONSTRAINT
ALTER TABLE T2 WITH CHECK ADD CONSTRAINT FK_T2_T1 FOREIGN KEY (FKID) REFERENCES T1 (ID); --fails
--Add the CONSTRAINT without checking existing values
ALTER TABLE T2 WITH NOCHECK ADD CONSTRAINT FK_T2_T1 FOREIGN KEY (FKID) REFERENCES T1 (ID); --succeeds
ALTER TABLE T2 CHECK CONSTRAINT FK_T2_T1; --succeeds since the CONSTRAINT is attributed as NOCHECK
--Attempt to enable CONSTRAINT fails due to orphans
ALTER TABLE T2 WITH CHECK CHECK CONSTRAINT FK_T2_T1; --fails
--Remove orphans
DELETE FROM T2 WHERE FKID NOT IN (SELECT ID FROM T1);
--Enabling the CONSTRAINT succeeds
ALTER TABLE T2 WITH CHECK CHECK CONSTRAINT FK_T2_T1; --succeeds; orphans removed
--Clean up
DROP TABLE T2;
DROP TABLE T1;

Further to the above excellent comments about trusted constraints:
select * from sys.foreign_keys where is_not_trusted = 1 ;
select * from sys.check_constraints where is_not_trusted = 1 ;
An untrusted constraint, much as its name suggests, cannot be trusted to accurately represent the state of the data in the table right now. It can, however, but can be trusted to check data added and modified in the future.
Additionally, untrusted constraints are disregarded by the query optimiser.
The code to enable check constraints and foreign key constraints is pretty bad, with three meanings of the word "check".
ALTER TABLE [Production].[ProductCostHistory]
WITH CHECK -- This means "Check the existing data in the table".
CHECK CONSTRAINT -- This means "enable the check or foreign key constraint".
[FK_ProductCostHistory_Product_ProductID] -- The name of the check or foreign key constraint, or "ALL".

WITH NOCHECK is used as well when one has existing data in a table that doesn't conform to the constraint as defined and you don't want it to run afoul of the new constraint that you're implementing...

WITH CHECK is indeed the default behaviour however it is good practice to include within your coding.
The alternative behaviour is of course to use WITH NOCHECK, so it is good to explicitly define your intentions. This is often used when you are playing with/modifying/switching inline partitions.

Foreign key and check constraints have the concept of being trusted or untrusted, as well as being enabled and disabled. See the MSDN page for ALTER TABLE for full details.
WITH CHECK is the default for adding new foreign key and check constraints, WITH NOCHECK is the default for re-enabling disabled foreign key and check constraints. It's important to be aware of the difference.
Having said that, any apparently redundant statements generated by utilities are simply there for safety and/or ease of coding. Don't worry about them.

Here is some code I wrote to help us identify and correct untrusted CONSTRAINTs in a DATABASE. It generates the code to fix each issue.
;WITH Untrusted (ConstraintType, ConstraintName, ConstraintTable, ParentTable, IsDisabled, IsNotForReplication, IsNotTrusted, RowIndex) AS
(
SELECT
'Untrusted FOREIGN KEY' AS FKType
, fk.name AS FKName
, OBJECT_NAME( fk.parent_object_id) AS FKTableName
, OBJECT_NAME( fk.referenced_object_id) AS PKTableName
, fk.is_disabled
, fk.is_not_for_replication
, fk.is_not_trusted
, ROW_NUMBER() OVER (ORDER BY OBJECT_NAME( fk.parent_object_id), OBJECT_NAME( fk.referenced_object_id), fk.name) AS RowIndex
FROM
sys.foreign_keys fk
WHERE
is_ms_shipped = 0
AND fk.is_not_trusted = 1
UNION ALL
SELECT
'Untrusted CHECK' AS KType
, cc.name AS CKName
, OBJECT_NAME( cc.parent_object_id) AS CKTableName
, NULL AS ParentTable
, cc.is_disabled
, cc.is_not_for_replication
, cc.is_not_trusted
, ROW_NUMBER() OVER (ORDER BY OBJECT_NAME( cc.parent_object_id), cc.name) AS RowIndex
FROM
sys.check_constraints cc
WHERE
cc.is_ms_shipped = 0
AND cc.is_not_trusted = 1
)
SELECT
u.ConstraintType
, u.ConstraintName
, u.ConstraintTable
, u.ParentTable
, u.IsDisabled
, u.IsNotForReplication
, u.IsNotTrusted
, u.RowIndex
, 'RAISERROR( ''Now CHECKing {%i of %i)--> %s ON TABLE %s'', 0, 1'
+ ', ' + CAST( u.RowIndex AS VARCHAR(64))
+ ', ' + CAST( x.CommandCount AS VARCHAR(64))
+ ', ' + '''' + QUOTENAME( u.ConstraintName) + ''''
+ ', ' + '''' + QUOTENAME( u.ConstraintTable) + ''''
+ ') WITH NOWAIT;'
+ 'ALTER TABLE ' + QUOTENAME( u.ConstraintTable) + ' WITH CHECK CHECK CONSTRAINT ' + QUOTENAME( u.ConstraintName) + ';' AS FIX_SQL
FROM Untrusted u
CROSS APPLY (SELECT COUNT(*) AS CommandCount FROM Untrusted WHERE ConstraintType = u.ConstraintType) x
ORDER BY ConstraintType, ConstraintTable, ParentTable;

Dare I say it, it feels like it might be an SSMS (inverted logic) bug; in that the explicit inclusion/use of the 'WITH CHECK' would be needed for the 2nd (existing/reenabled constraint) statement, not the first (new/'with-check' defaulted).
I'm wondering whether they've just applied the generation of the 'WITH CHECK' clause to the wrong SQL statement / the 1st T-SQL statement rather than the 2nd one - assuming they're trying to default the use of the check for both scenarios - for both a new constraint or (the reenabling of) an existing one.
(Seems to make sense to me, as the longer a check constraint is disabled, the theoretically increased chance that broken/check-constraint-invalid data might have crept-in in the meantime.)

Related

Cannot drop index because of foreign key constraint enforcement

I can't drop an index because some tables used it for foreign key
Msg 3723, Level 16, State 6, Line 1 An explicit DROP INDEX is not
allowed on index 'tbl1.ix_cox'. It is being used for FOREIGN KEY
constraint enforcement.
I tried to disable the index first before dropping
ALTER INDEX ix_cox On tbl1
DISABLE
Go
But still don't able to drop it.
Do I really need to remove the foreign key on those tables that used that index? Because it is about 30 tables.
You'll get the same exception if the index was created defining a PRIMARY KEY or UNIQUE constraint (https://learn.microsoft.com/en-us/sql/t-sql/statements/drop-index-transact-sql?view=sql-server-2017).
In that case the simple solution is to use the ALTER-TABLE-command instead:
ALTER TABLE tbl1 DROP CONSTRAINT ix_cox
I am afraid this is the only option you have. you have to drop all foreign key constraints referencing to table and also you cannot recreate the foreign key constraints until you specify another unique index on the table from where you removed index.
Good day,
Here is a full example:
Let's create the tables and insert some sample data first
use tempdb
GO
-- NOTE!
-- This sample code presents a POOR CODING where the user
-- does not explicitly named the objects
-- or explicitly create CLUSTERED INDEX
DROP TABLE IF EXISTS dbo.Users_tbl;
DROP TABLE IF EXISTS dbo.Categories_tbl;
GO
CREATE TABLE dbo.Categories_tbl(
CategoryID INT IDENTITY(1,1) PRIMARY KEY
, CategoryName NVARCHAR(100)
)
GO
-- find the CLUSTERED INDEX created automatically for us
SELECT * FROM sys.indexes
WHERE object_id = OBJECT_ID('Categories_tbl')
GO
-- you can notice that by default the PRIMARY KEY become CLUSTERED INDEX
-- If we did not configure a different CLUSTERED INDEX
-- In my case the automatic name was: PK__Categori__19093A2BAE0EA4C3
-- Let's create the secondary table
CREATE TABLE dbo.Users_tbl(
UserID INT IDENTITY(1,1) PRIMARY KEY
, UserName NVARCHAR(100)
, CategoryID INT
, FOREIGN KEY (CategoryID) REFERENCES Categories_tbl(CategoryID)
)
GO
-- Insert sample data
INSERT Categories_tbl (CategoryName) VALUES ('a'),('b')
GO
INSERT Users_tbl(UserName,CategoryID)
VALUES ('a',1),('b',1)
GO
SELECT * FROM Categories_tbl
SELECT * FROM Users_tbl
GO
REMOVE CLUSTERED INDEX from PRIMARY KEY
If we will try to DROP INDEX of the PK then we will get this error:
An explicit DROP INDEX is not allowed on index... It is being used for PRIMARY KEY constraint enforcement.
The solution is to drop the FK, Drop the PK, Create new PK with NONCLUSTERED index instead of CLUSTERED, and create the FK
/************************************************ */
/********* REMOVE CLUSTERED INDEX from PRIMARY KEY */
/************************************************ */
------------------------------------------------------
-- Step 1: DROP the CONSTRAINTs
------------------------------------------------------
---- Get FOREIGN KEY name
SELECT DISTINCT OBJECT_NAME(f.constraint_object_id)
FROM sys.foreign_key_columns f
LEFT JOIN sys.indexes p ON p.object_id = f.referenced_object_id
WHERE p.object_id = OBJECT_ID('Categories_tbl')
GO
-- DROP FOREIGN KEY
ALTER TABLE dbo.Users_tbl
DROP CONSTRAINT FK__Users_tbl__Categ__59063A47 -- Use the name we found above
GO
---- Get PRIMARY KEY name
SELECT name FROM sys.indexes
WHERE object_id = OBJECT_ID('Categories_tbl')
GO
-- DROP PRIMARY KEY
ALTER TABLE dbo.Categories_tbl
DROP CONSTRAINT PK__Categori__19093A2B9F118674 -- Use the name we found above
GO
------------------------------------------------------
-- Step 2: CREATE new CONSTRAINTs
------------------------------------------------------
-- And now we can create new PRIMARY KEY NONCLUSTERED
-- Since we use PRIMARY KEY We need to have index,
-- but we do not have to use CLUSTERED INDEX
-- we can have NONCLUSTERED INDEX
ALTER TABLE dbo.Categories_tbl
ADD CONSTRAINT PK_CategoryID PRIMARY KEY NONCLUSTERED (CategoryID);
GO
-- Finaly we can create the
ALTER TABLE dbo.Users_tbl
ADD CONSTRAINT FK_Categories_tbl
FOREIGN KEY (CategoryID)
REFERENCES dbo.Categories_tbl(CategoryID)
GO
You can disable the FK constraint for temporary being -> drop the index -> re-enable the constraint back like
ALTER TABLE your_fk_table NOCHECK CONSTRAINT constraint_name
drop index ids_name
ALTER TABLE your_fk_table CHECK CONSTRAINT constraint_name
Well, if you have an FK on 30 tables like EzLo, it would sure be nice to have some automation. The script in the last two columns of this view will drop and re-add your FK's:
CREATE VIEW vwFK
AS
select *, addFK = 'ALTER TABLE '+FKtable+' WITH ' + case when is_not_trusted = 1 then 'NO' else '' end + 'CHECK'
+ ' ADD CONSTRAINT [FK_'+FKtbl+'_'+PKtbl+'] FOREIGN KEY ('+FKcol+') '
+ ' REFERENCES '+PKtable+' ('+PKcol+')'+' ON UPDATE '+onUpdate+' ON DELETE '+onDelete
+ case when is_not_for_replication = 1 then ' NOT FOR REPLICATION' else '' end + ';'
+ case when is_disabled = 1 then ' ALTER TABLE '+FKtable+' NOCHECK CONSTRAINT [FK_'+FKtbl+'_'+PKtbl+'];' else '' end
,dropFK = 'ALTER TABLE '+FKtable+' DROP ['+FK+'];'
from (
select
PKtable = object_schema_name(f.referenced_object_id)+'.'+object_name(f.referenced_object_id)
,PKtbl = object_name(f.referenced_object_id)
,PKcol = pc.name
,FKtable = object_schema_name(f.parent_object_id)+'.'+object_name(f.parent_object_id)
,FKtbl = object_name(f.parent_object_id)
,colseq = fk.constraint_column_id
,FKcol = fc.name
,FK = object_name(f.object_id)
,onUpdate = replace(f.update_referential_action_desc collate SQL_Latin1_General_CP1_CI_AS, '_', ' ')
,onDelete = replace(f.delete_referential_action_desc collate SQL_Latin1_General_CP1_CI_AS, '_', ' ')
,f.is_disabled
,f.is_not_trusted
,f.is_not_for_replication
from sys.foreign_key_columns as fk
join sys.foreign_keys f on fk.constraint_object_id = f.object_id
join sys.columns as fc on f.parent_object_id = fc.object_id and fk.parent_column_id = fc.column_id
join sys.columns as pc on f.referenced_object_id = pc.object_id and fk.referenced_column_id = pc.column_id
) t
No guarantees, but this has saved me a lot of work over the years. Just remember to save the addFK statements before running your dropFK's!

Multiple constraints in table: How to get all violations?

I have a table in Oracle with several constraints. When I insert a new record and not all constraints are valid, then Oracle raise only the "first" error. How to get all violations of my record?
CREATE TABLE A_TABLE_TEST (
COL_1 NUMBER NOT NULL,
COL_2 NUMBER NOT NULL,
COL_3 NUMBER NOT NULL,
COL_4 NUMBER NOT NULL
);
INSERT INTO A_TABLE_TEST values (1,null,null,2);
ORA-01400: cannot insert NULL into ("USER_4_8483C"."A_TABLE_TEST"."COL_2")
I would like to get something like this:
Column COL_2: cannot insert NULL
Column COL_3: cannot insert NULL
This would be also sufficient:
Column COL_2: not valid
Column COL_3: not valid
Of course I could write a trigger and check each column individually, but I like to prefer constraints rather than triggers, they are easier to maintain and don't require manually written code.
Any idea?
There no straightforward way to report all possible constraint violations. Because when Oracle stumble on first violation of a constraint, no further evaluation is possible, statement fails, unless that constraint is deferred one or the log errors clause has been included in the DML statement. But it should be noted that log errors clause won't be able to catch all possible constraint violations, just records first one.
As one of the possible ways is to:
create exceptions table. It can be done by executing ora_home/rdbms/admin/utlexpt.sql script. The table's structure is pretty simple;
disable all table constraints;
execute DMLs;
enable all constraints with exceptions into <<exception table name>> clause. If you executed utlexpt.sql script, the name of the table exceptions are going to be stored would be exceptions.
Test table:
create table t1(
col1 number not null,
col2 number not null,
col3 number not null,
col4 number not null
);
Try to execute an insert statement:
insert into t1(col1, col2, col3, col4)
values(1, null, 2, null);
Error report -
SQL Error: ORA-01400: cannot insert NULL into ("HR"."T1"."COL2")
Disable all table's constraints:
alter table T1 disable constraint SYS_C009951;
alter table T1 disable constraint SYS_C009950;
alter table T1 disable constraint SYS_C009953;
alter table T1 disable constraint SYS_C009952;
Try to execute the previously failed insert statement again:
insert into t1(col1, col2, col3, col4)
values(1, null, 2, null);
1 rows inserted.
commit;
Now, enable table's constraints and store exceptions, if there are any, in the exceptions table:
alter table T1 enable constraint SYS_C009951 exceptions into exceptions;
alter table T1 enable constraint SYS_C009950 exceptions into exceptions;
alter table T1 enable constraint SYS_C009953 exceptions into exceptions;
alter table T1 enable constraint SYS_C009952 exceptions into exceptions;
Check the exceptions table:
column row_id format a30;
column owner format a7;
column table_name format a10;
column constraint format a12;
select *
from exceptions
ROW_ID OWNER TABLE_NAME CONSTRAINT
------------------------------ ------- ------- ------------
AAAWmUAAJAAAF6WAAA HR T1 SYS_C009951
AAAWmUAAJAAAF6WAAA HR T1 SYS_C009953
Two constraints have been violated. To find out column names, simply refer to user_cons_columns data dictionary view:
column table_name format a10;
column column_name format a7;
column row_id format a20;
select e.table_name
, t.COLUMN_NAME
, e.ROW_ID
from user_cons_columns t
join exceptions e
on (e.constraint = t.constraint_name)
TABLE_NAME COLUMN_NAME ROW_ID
---------- ---------- --------------------
T1 COL2 AAAWmUAAJAAAF6WAAA
T1 COL4 AAAWmUAAJAAAF6WAAA
The above query gives us column names, and rowids of problematic records. Having rowids at hand, there should be no problem to find those records that cause constraint violation, fix them, and re-enable constraints once again.
Here is the script that has been used to generate alter table statements for enabling and disabling constraints:
column cons_disable format a50
column cons_enable format a72
select 'alter table ' || t.table_name || ' disable constraint '||
t.constraint_name || ';' as cons_disable
, 'alter table ' || t.table_name || ' enable constraint '||
t.constraint_name || ' exceptions into exceptions;' as cons_enable
from user_constraints t
where t.table_name = 'T1'
order by t.constraint_type
You would have to implement a before-insert trigger to loop through all the conditions that you care about.
Think about the situation from the database's perspective. When you do an insert, the database can basically do two things: complete the insert successfully or fail for some reason (typically a constraint violation).
The database wants to proceed as quickly as possibly and not do unnecessary work. Once it has found the first complaint violation, it knows that the record is not going into the database. So, the engine wisely returns an error and stops checking further constraints. There is no reason for the engine to get the full list of violations.
In the meantime I found a lean solution using deferred constraints:
CREATE TABLE A_TABLE_TEST (
COL_1 NUMBER NOT NULL DEFERRABLE INITIALLY DEFERRED,
COL_2 NUMBER NOT NULL DEFERRABLE INITIALLY DEFERRED,
COL_3 NUMBER NOT NULL DEFERRABLE INITIALLY DEFERRED,
COL_4 NUMBER NOT NULL DEFERRABLE INITIALLY DEFERRED
);
INSERT INTO A_TABLE_TEST values (1,null,null,2);
DECLARE
CHECK_CONSTRAINT_VIOLATED EXCEPTION;
PRAGMA EXCEPTION_INIT(CHECK_CONSTRAINT_VIOLATED, -2290);
REF_CONSTRAINT_VIOLATED EXCEPTION;
PRAGMA EXCEPTION_INIT(REF_CONSTRAINT_VIOLATED , -2292);
CURSOR CheckConstraints IS
SELECT TABLE_NAME, CONSTRAINT_NAME, COLUMN_NAME
FROM USER_CONSTRAINTS
JOIN USER_CONS_COLUMNS USING (TABLE_NAME, CONSTRAINT_NAME)
WHERE TABLE_NAME = 'A_TABLE_TEST'
AND DEFERRED = 'DEFERRED'
AND STATUS = 'ENABLED';
BEGIN
FOR aCon IN CheckConstraints LOOP
BEGIN
EXECUTE IMMEDIATE 'SET CONSTRAINT '||aCon.CONSTRAINT_NAME||' IMMEDIATE';
EXCEPTION
WHEN CHECK_CONSTRAINT_VIOLATED OR REF_CONSTRAINT_VIOLATED THEN
DBMS_OUTPUT.PUT_LINE('Constraint '||aCon.CONSTRAINT_NAME||' at Column '||aCon.COLUMN_NAME||' violated');
END;
END LOOP;
END;
It works with any check constraint (not only NOT NULL). Checking FOREIGN KEY Constraint should work as well.
Add/Modify/Delete of constraints does not require any further maintenance.

SQL Server 2008 Script to Drop PK Constraint that has a System Generated Name

I am trying to add a clustered index to an existing table in SQL Server 2008, and it needs to be an automated script, because this table exists on several databases across several servers.
In order to add a clustered index I need to remove the PK constraint on the table, and then re-add it as unclustered. The problem is the name of the PK constraint is auto-generated, and there is a guid appended to the end, so it's like "PK_[Table]_D9F9203400."
The name is different across all databases, and I'm not sure how to write an automated script that drops a PK constraint on a table in which I don't know the name of the constraint. Any help is appreciated!
UPDATE:
Answer below is what I used. Full script:
Declare #Val varchar(100)
Declare #Cmd varchar(1000)
Set #Val = (
select name
from sysobjects
where xtype = 'PK'
and parent_obj = (object_id('[Schema].[Table]'))
)
Set #Cmd = 'ALTER TABLE [Table] DROP CONSTRAINT ' + #Val
Exec (#Cmd)
GO
ALTER TABLE [Table] ADD CONSTRAINT PK_Table
PRIMARY KEY NONCLUSTERED (TableId)
GO
CREATE UNIQUE CLUSTERED INDEX IX_Table_Column
ON Table (Column)
GO
You can look up the name of the constraint and write a bit of dynamic SQL to handle the drop.
SELECT name
FROM sys.key_constraints
WHERE parent_object_id = object_id('YourSchemaName.YourTableName')
AND type = 'PK';

SQL Server 2005: Nullable Foreign Key Constraint

I have a foreign key constraint between tables Sessions and Users. Specifically, Sessions.UID = Users.ID. Sometimes, I want Sessions.UID to be null. Can this be allowed? Any time I try to do this, I get an FK Constraint Violation.
Specifically, I'm inserting a row into Sessions via LINQ. I set the Session.User = null; and I get this error:
An attempt was made to remove a relationship between a User and a Session. However, one of the relationship's foreign keys (Session.UID) cannot be set to null.
However, when I remove the line that nulls the User property, I get this error on my SubmitChanges line:
Value cannot be null.
Parameter name: cons
None of my tables have a field called 'cons', nor is it in my 5,500-line DataContext.designer.cs file, nor is it in the QuickWatch for any of the related objects, so I have no idea what 'cons' is.
In the Database, Session.UID is a nullable int field and User.ID is a non-nullable int. I want to record sessions that may or may not have a UID, and I'd rather do it without disabling constraint on that FK relationship. Is there a way to do this?
I seemed to remember creating a nullable FK before, so I whipped up a quick test. As you can see below, it is definitely doable (tested on MSSQL 2005).
Script the relevant parts of your tables and constraints and post them so we can troubleshoot further.
CREATE DATABASE [NullableFKTest]
GO
USE [NullableFKTest]
GO
CREATE TABLE OneTable
(
OneId [int] NOT NULL,
CONSTRAINT [PK_OneTable] PRIMARY KEY CLUSTERED
(
[OneId] ASC
)
)
CREATE TABLE ManyTable (ManyId [int] IDENTITY(1,1) NOT NULL, OneId [int] NULL)
GO
IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_ManyTable_OneTable]') AND parent_object_id = OBJECT_ID(N'[dbo].[ManyTable]') )
ALTER TABLE [dbo].[ManyTable] WITH CHECK ADD CONSTRAINT [FK_ManyTable_OneTable] FOREIGN KEY([OneId])
REFERENCES [dbo].[OneTable] ([OneId])
GO
--let's get a value in here
insert into OneTable(OneId) values(1)
select* from OneTable
--let's try creating a valid relationship to the FK table OneTable
insert into ManyTable(OneId) values (1) --fine
--now, let's try NULL
insert into ManyTable(OneId) values (NULL) --also fine
--how about a non-existent OneTable entry?
insert into ManyTable(OneId) values (5) --BOOM! - FK violation
select* from ManyTable
--1, 1
--2, NULL
--cleanup
ALTER TABLE ManyTable DROP CONSTRAINT FK_ManyTable_OneTable
GO
drop TABLE OneTable
GO
drop TABLE ManyTable
GO
USE [Master]
GO
DROP DATABASE NullableFKTest

In SQL Server 2005, how do I set a column of integers to ensure values are greater than 0?

This is probably a simple answer but I can't find it. I have a table with a column of integers and I want to ensure that when a row is inserted that the value in this column is greater than zero. I could do this on the code side but thought it would be best to enforce it on the table.
Thanks!
I was in error with my last comment all is good now.
You can use a check constraint on the column. IIRC the syntax for this looks like:
create table foo (
[...]
,Foobar int not null check (Foobar > 0)
[...]
)
As the poster below says (thanks Constantin), you should create the check constraint outside the table definition and give it a meaningful name so it is obvious which column it applies to.
alter table foo
add constraint Foobar_NonNegative
check (Foobar > 0)
You can get out the text of check constraints from the system data dictionary in sys.check_constraints:
select name
,description
from sys.check_constraints
where name = 'Foobar_NonNegative'
Create a database constraint:
ALTER TABLE Table1 ADD CONSTRAINT Constraint1 CHECK (YourCol > 0)
You can have pretty sophisticated constraints, too, involving multiple columns. For example:
ALTER TABLE Table1 ADD CONSTRAINT Constraint2 CHECK (StartDate<EndDate OR EndDate IS NULL)
I believe you want to add a CONSTRAINT to the table field:
ALTER TABLE tableName WITH NOCHECK
ADD CONSTRAINT constraintName CHECK (columnName > 0)
That optional NOCHECK is used to keep the constraint from being applied to existing rows of data (which could contain invalid data) & to allow the constraint to be added.
Add a CHECK constraint when creating your table
CREATE TABLE Test(
[ID] [int] NOT NULL,
[MyCol] [int] NOT NULL CHECK (MyCol > 1)
)
you can alter your table and add new constraint like bellow.
BEGIN TRANSACTION
GO
ALTER TABLE dbo.table1 ADD CONSTRAINT
CK_table1_field1 CHECK (field1>0)
GO
ALTER TABLE dbo.table1 SET (LOCK_ESCALATION = TABLE)
GO
COMMIT