| name | sqlserver-schema |
| description | SQL Server DDL patterns — CREATE TABLE with proper data types, ALTER TABLE, idempotent migrations, foreign keys, computed columns, and check constraints. |
SQL Server Schema Management
Use this skill for creating and modifying database schemas, writing migration scripts, and applying DDL best practices.
CREATE TABLE — Data Type Guide
CREATE TABLE dbo.Customers (
CustomerID INT NOT NULL IDENTITY(1,1),
ExternalRef BIGINT NULL,
CompanyName NVARCHAR(200) NOT NULL,
CountryCode CHAR(2) NOT NULL,
Notes NVARCHAR(MAX) NULL,
CreatedAt DATETIME2(7) NOT NULL DEFAULT GETUTCDATE(),
UpdatedAt DATETIME2(7) NULL,
BirthDate DATE NULL,
AppointmentTime TIME(0) NULL,
AccountBalance DECIMAL(18,4) NOT NULL DEFAULT 0,
IsActive BIT NOT NULL DEFAULT 1,
PublicToken UNIQUEIDENTIFIER NOT NULL DEFAULT NEWSEQUENTIALID(),
CONSTRAINT PK_Customers PRIMARY KEY CLUSTERED (CustomerID),
CONSTRAINT UQ_Customers_CompanyName UNIQUE (CompanyName),
CONSTRAINT CK_Customers_CountryCode CHECK (LEN(CountryCode) = 2)
);
Data Type Quick Reference
| Use Case | Type | Notes |
|---|
| Row ID / PK | INT IDENTITY | Up to 2.1 billion rows |
| Large row ID / PK | BIGINT IDENTITY | Up to 9.2 quintillion rows |
| Unicode text | NVARCHAR(n) | n ≤ 4000; use MAX sparingly |
| ASCII-only text | VARCHAR(n) | Half the storage of NVARCHAR |
| Fixed-length string | CHAR(n) or NCHAR(n) | ISO codes, padded fields |
| Currency / exact decimal | DECIMAL(p,s) or MONEY | Never FLOAT |
| Date + time | DATETIME2(7) | Prefer over legacy DATETIME |
| Date only | DATE | |
| Time only | TIME(n) | n = fractional seconds precision |
| Boolean | BIT | 0/1/NULL |
| GUID | UNIQUEIDENTIFIER | Use NEWSEQUENTIALID() as default |
| Large binary | VARBINARY(MAX) | Files, images (prefer file storage) |
| JSON / XML text | NVARCHAR(MAX) | Or native XML type for querying |
DATETIME vs DATETIME2:
DATETIME — legacy, 3.33ms precision, range 1753–9999
DATETIME2 — preferred, 100ns precision, range 0001–9999, ANSI SQL compliant
ALTER TABLE
ALTER TABLE dbo.Customers ADD PhoneNumber NVARCHAR(50) NULL;
ALTER TABLE dbo.Customers ADD IsVerified BIT NOT NULL DEFAULT 0;
ALTER TABLE dbo.Customers ADD Region NVARCHAR(50) NULL;
UPDATE dbo.Customers SET Region = 'Unknown' WHERE Region IS NULL;
ALTER TABLE dbo.Customers ALTER COLUMN Region NVARCHAR(50) NOT NULL;
ALTER TABLE dbo.Customers ADD CONSTRAINT CK_Customers_Balance CHECK (AccountBalance >= 0);
ALTER TABLE dbo.Orders ADD CONSTRAINT FK_Orders_Customers
FOREIGN KEY (CustomerID) REFERENCES dbo.Customers (CustomerID);
CREATE INDEX IX_Customers_Region ON dbo.Customers (Region) INCLUDE (CompanyName, IsActive);
ALTER TABLE dbo.Customers DROP PhoneNumber;
dbo.Customers CK_Customers_Balance;
sp_rename , , ;
Migration Scripts — Idempotent Patterns
Write migrations so they can be re-run safely. This is critical for deployments.
IF NOT EXISTS (
SELECT 1 FROM sys.columns
WHERE object_id = OBJECT_ID('dbo.Customers') AND name = 'Region'
)
BEGIN
ALTER TABLE dbo.Customers ADD Region NVARCHAR(50) NULL;
END
GO
IF NOT EXISTS (
SELECT 1 FROM sys.indexes
WHERE object_id = OBJECT_ID('dbo.Customers') AND name = 'IX_Customers_Region'
)
BEGIN
CREATE INDEX IX_Customers_Region ON dbo.Customers (Region);
END
GO
IF NOT EXISTS (
SELECT 1 FROM sys.check_constraints
WHERE name = 'CK_Customers_Balance' AND parent_object_id = OBJECT_ID('dbo.Customers')
)
BEGIN
ALTER TABLE dbo.Customers ADD CONSTRAINT CK_Customers_Balance CHECK (AccountBalance );
GO
IF ( sys.tables name schema_id SCHEMA_ID())
dbo.AuditLog (
AuditID (,),
TableName NVARCHAR() ,
Operation () ,
ChangedAt DATETIME2() GETUTCDATE(),
ChangedBy NVARCHAR() ,
PK_AuditLog CLUSTERED (AuditID)
);
GO
IF OBJECT_ID(, )
dbo.usp_OldProcedure;
GO
Migration Numbering Pattern
migrations/
├── V001__create_customers_table.sql
├── V002__add_region_column.sql
├── V003__create_orders_table.sql
├── V004__add_orders_fk_customers.sql
Each file:
- Prefixed with
V + zero-padded number + __ + description
- Idempotent (safe to re-run)
- Contains only forward changes (no rollback in same file)
- Tested on a staging database before production
Foreign Key Patterns
ALTER TABLE dbo.OrderItems ADD CONSTRAINT FK_OrderItems_Orders
FOREIGN KEY (OrderID) REFERENCES dbo.Orders (OrderID)
ON DELETE CASCADE
ON UPDATE NO ACTION;
ALTER TABLE dbo.Orders ADD CONSTRAINT FK_Orders_Customers
FOREIGN KEY (CustomerID) REFERENCES dbo.Customers (CustomerID)
ON DELETE SET NULL;
ALTER TABLE dbo.Orders ADD CONSTRAINT FK_Orders_Customers
FOREIGN KEY (CustomerID) REFERENCES dbo.Customers (CustomerID);
ALTER TABLE dbo.OrderItems NOCHECK CONSTRAINT FK_OrderItems_Orders;
ALTER TABLE dbo.OrderItems WITH CHECK CHECK CONSTRAINT FK_OrderItems_Orders;
Computed Columns
ALTER TABLE dbo.Orders ADD
TaxAmount AS (TotalAmount * 0.08) PERSISTED,
FullAmount AS (TotalAmount + TotalAmount * 0.08) PERSISTED;
ALTER TABLE dbo.Products ADD
SearchName AS LOWER(TRIM(ProductName)) PERSISTED;
CREATE INDEX IX_Products_SearchName ON dbo.Products (SearchName);
Check Constraints
ALTER TABLE dbo.Products ADD CONSTRAINT CK_Products_Price
CHECK (Price >= 0);
ALTER TABLE dbo.Orders ADD CONSTRAINT CK_Orders_Status
CHECK (Status IN ('Pending', 'Processing', 'Shipped', 'Delivered', 'Cancelled'));
ALTER TABLE dbo.Events ADD CONSTRAINT CK_Events_Dates
CHECK (EndDate >= StartDate);
ALTER TABLE dbo.Customers ADD CONSTRAINT CK_Customers_Email
CHECK (Email LIKE '%@%.%');