CREATE TABLE and constraints: expressing rules from the schema
A table is not just a list of columns. It must also express how each row is identified, what relationships are valid, and what values it can accept.
Constraints are rules enforced by the table. They protect the model even when data arrives from different applications, imports, or scripts.
Main elements
IDENTITY: generate a numeric value
IDENTITY asks SQL Server to generate a number for every new row. The first argument sets the initial value and the second sets the increment.
Id BIGINT IDENTITY(1, 1) NOT NULL
PRIMARY KEY: identify every row
PRIMARY KEY requires the column or column combination to be unique and not accept NULL. A table can have only one primary key.
CONSTRAINT PK_Tabla
PRIMARY KEY (Id)
FOREIGN KEY: require a valid reference
FOREIGN KEY relates columns in the current table to a candidate key in another table and rejects references to nonexistent rows.
CONSTRAINT FK_Tabla_Relacion
FOREIGN KEY (IdRelacionado)
REFERENCES dbo.TablaRelacionada (IdRelacionado)
A candidate key is a column or column combination capable of identifying a row through a primary or unique constraint.
UNIQUE: prevent repeated combinations
UNIQUE prevents the specified value combination from repeating. It can express a rule other than the primary key, such as preventing a customer from marking the same product twice.
CONSTRAINT UQ_Tabla_Columnas
UNIQUE (Columna1, Columna2)
CHECK: validate a condition
CHECK accepts a row when its condition is not false. We must therefore consider what happens with NULL and combine it with nullability when the rule requires a value.
CONSTRAINT CK_Tabla_Regla
CHECK (Condicion)
DEFAULT: provide a value when a column is omitted
DEFAULT supplies a value when an INSERT does not include the column. It does not replace an explicitly supplied NULL.
CONSTRAINT DF_Tabla_Columna
DEFAULT Expresion
The example uses SYSDATETIME() as the registry default value. The function gets the current date and time from the server and returns a value compatible with DATETIME2.
Example: table with explicit rules
CREATE TABLE dbo.ProductoFavorito
(
IdFavorito BIGINT IDENTITY(1, 1) NOT NULL,
IdCliente INT NOT NULL,
IdProducto INT NOT NULL,
Prioridad TINYINT NOT NULL
CONSTRAINT DF_ProductoFavorito_Prioridad DEFAULT 1,
FechaRegistro DATETIME2(0) NOT NULL
CONSTRAINT DF_ProductoFavorito_Fecha DEFAULT SYSDATETIME(),
CONSTRAINT PK_ProductoFavorito
PRIMARY KEY (IdFavorito),
CONSTRAINT UQ_ProductoFavorito_ClienteProducto
UNIQUE (IdCliente, IdProducto),
CONSTRAINT CK_ProductoFavorito_Prioridad
CHECK (Prioridad BETWEEN 1 AND 5),
CONSTRAINT FK_ProductoFavorito_Cliente
FOREIGN KEY (IdCliente)
REFERENCES dbo.Cliente (IdCliente),
CONSTRAINT FK_ProductoFavorito_Producto
FOREIGN KEY (IdProducto)
REFERENCES dbo.Producto (IdProducto)
);
The primary key identifies each row uniquely. The unique constraint prevents a combination of values from being repeated. Foreign keys require a reference to point to an existing row; here, to valid customers and products.
IDENTITY does not replace a key
IDENTITY generates a number, but does not automatically create a PRIMARY KEY constraint nor does it itself guarantee that the value represents the business rule.
Breaks in a IDENTITY column are normal. We should not promise a sequence without gaps or use it as a legal or documentary consecutive number without specifically designing that requirement.
Default values
DEFAULT is used when the column is omitted in INSERT. It does not automatically replace an explicitly sent NULL.
Naming constraints makes it easier to find and modify them later. Names should follow a consistent convention within the database.
Choose types and nullability
The type must represent actual range, precision, and length. NOT NULL expresses that a valid row always needs that data; allowing nulls for convenience carries ambiguity to all subsequent queries.
A text should also not be given a maximum length out of habit. The choice affects storage, indexes, validation, and integration with other systems.
What should be checked
Before creating the table, we review names, schema, collation, expected growth, business keys, and query patterns. Collation defines how text is compared and sorted. A business key identifies an entity with domain data, such as an official number, rather than only an internal identifier. Constraints must represent true rules, not temporary coincidences in current data.
A foreign key protects referential integrity: it prevents references to nonexistent rows. It does not necessarily create the most suitable index on child columns; an index organizes values to make rows easier to access. We must evaluate expected access and deletions.