[{"data":1,"prerenderedAt":111},["ShallowReactive",2],{"qa-\u002Fsql\u002Fschema\u002Fconstraints":3},{"page":4,"siblings":95,"blog":108},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":85,"related":86,"seo":87,"seoDescription":88,"stem":89,"subtopic":90,"topic":91,"topicSlug":92,"updated":93,"__hash__":94},"qa\u002Fsql\u002Fschema\u002Fconstraints.md","Constraints",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","SQL","sql",{},true,3,"\u002Fsql\u002Fschema\u002Fconstraints",[23,28,32,36,40,44,48,52,57,61,65,69,73,77,81],{"id":24,"difficulty":25,"q":26,"a":27},"what-is-constraint","easy","What is a constraint and why use one?","A **constraint** is a rule enforced by the database engine that limits\nwhat values can be stored in a column or set of columns. Constraints catch\nbad data at write time — before it ever enters the database — so application\ncode never has to defensively re-validate what the schema already guarantees.\n\n```sql\nCREATE TABLE employees (\n  id         INT  GENERATED ALWAYS AS IDENTITY PRIMARY KEY, -- unique, not null\n  email      TEXT NOT NULL UNIQUE,                          -- no nulls, no dupes\n  salary     NUMERIC(10,2) CHECK (salary > 0),             -- must be positive\n  dept_id    INT  REFERENCES departments(id)               -- must exist in parent\n);\n```\n\n**Rule of thumb:** encode every invariant you can in the schema. A constraint\nthat runs in 0 ms at insert time is cheaper than debugging corrupt data in\nproduction.\n",{"id":29,"difficulty":25,"q":30,"a":31},"primary-key","What is a PRIMARY KEY constraint?","A **PRIMARY KEY** uniquely identifies each row in a table. It is a\ncombination of two implied constraints: `UNIQUE` (no two rows share the same\nvalue) and `NOT NULL` (the key column(s) can never be `NULL`). Each table\ncan have **at most one** primary key.\n\n```sql\n-- Single-column PK\nCREATE TABLE users (\n  id   INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n  name TEXT NOT NULL\n);\n\n-- Composite PK (natural key)\nCREATE TABLE order_items (\n  order_id   INT NOT NULL,\n  product_id INT NOT NULL,\n  quantity   INT NOT NULL,\n  PRIMARY KEY (order_id, product_id)\n);\n```\n\nThe database automatically creates a **unique index** on the PK column(s),\nwhich makes lookups by PK fast.\n\n**Rule of thumb:** every table should have a primary key. Prefer a single\nsurrogate integer\u002FUUID PK; use composite PKs for pure join tables\n(many-to-many mappings).\n",{"id":33,"difficulty":25,"q":34,"a":35},"foreign-key","What is a FOREIGN KEY constraint and what does it enforce?","A **FOREIGN KEY** (FK) constraint ensures that every non-NULL value in the\nreferencing column exists in the referenced column of the parent table.\nIt enforces **referential integrity** — you cannot have an `order` that\npoints to a `customer_id` that does not exist.\n\n```sql\nCREATE TABLE orders (\n  id          INT  GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n  customer_id INT  NOT NULL REFERENCES customers(id),\n  total       NUMERIC(10,2) NOT NULL\n);\n\n-- Explicit form with named constraint\nALTER TABLE orders\n  ADD CONSTRAINT fk_orders_customer\n  FOREIGN KEY (customer_id) REFERENCES customers(id);\n```\n\nOn insert, the DB checks that `customer_id` exists in `customers.id`. On\ndelete of a customer row, the FK's **referential action** decides what\nhappens (see CASCADE \u002F RESTRICT question).\n\n**Rule of thumb:** always add FK constraints on foreign-key columns — they\nare the database's guarantee that your data is consistent, not just a\ndocumentation comment.\n",{"id":37,"difficulty":14,"q":38,"a":39},"referential-actions","What are ON DELETE \u002F ON UPDATE referential actions on a foreign key?","Referential actions define what happens to child rows when the parent row\nis deleted or its PK updated:\n\n| Action | Effect on child rows |\n|---|---|\n| `RESTRICT` | Raises an error — cannot delete\u002Fupdate if children exist |\n| `NO ACTION` | Like RESTRICT but checked at end of statement (default) |\n| `CASCADE` | Deletes (or updates) all matching child rows automatically |\n| `SET NULL` | Sets the FK column(s) to `NULL` in child rows |\n| `SET DEFAULT` | Sets the FK column(s) to their default value |\n\n```sql\nCREATE TABLE order_items (\n  id         INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n  order_id   INT NOT NULL\n    REFERENCES orders(id) ON DELETE CASCADE,  -- items gone when order deleted\n  product_id INT NOT NULL\n    REFERENCES products(id) ON DELETE RESTRICT -- block if items still reference product\n);\n```\n\n**Rule of thumb:** use `ON DELETE CASCADE` for owned child records (items,\nline-items, comments). Use `RESTRICT` for shared reference data you never\nwant to accidentally wipe. Avoid `SET NULL` unless `NULL` is semantically\nmeaningful in the child.\n",{"id":41,"difficulty":25,"q":42,"a":43},"unique-constraint","What is a UNIQUE constraint and how does it differ from a PRIMARY KEY?","A **UNIQUE** constraint ensures that no two rows have the same value(s) in\nthe constrained column(s). Unlike `PRIMARY KEY`, a table can have **multiple**\nunique constraints, and unique columns **can** contain `NULL` (Postgres and\nSQL Server treat each `NULL` as distinct; MySQL treats `NULL = NULL`).\n\n```sql\nCREATE TABLE users (\n  id       INT  GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n  email    TEXT NOT NULL UNIQUE,\n  username TEXT NOT NULL UNIQUE,\n  phone    TEXT UNIQUE          -- nullable; two users can have NULL phone\n);\n\n-- Multi-column unique constraint\nALTER TABLE team_members\n  ADD CONSTRAINT uq_team_user UNIQUE (team_id, user_id);\n```\n\nA `UNIQUE` constraint creates a **unique index** automatically, so it also\nspeeds up equality lookups on those columns.\n\n**Rule of thumb:** add `UNIQUE` to every natural business key (email,\nusername, SSN) in addition to the surrogate PK — it is the database's\nguarantee that your deduplication logic is not bypassed.\n",{"id":45,"difficulty":14,"q":46,"a":47},"check-constraint","What is a CHECK constraint and what can it express?","A **CHECK** constraint specifies a boolean expression that every row must\nsatisfy. The insert or update is rejected if the expression evaluates to\n`FALSE`. (`NULL` evaluates to `UNKNOWN` and is allowed through by default.)\n\n```sql\nCREATE TABLE products (\n  id       INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n  price    NUMERIC(10,2) NOT NULL CHECK (price >= 0),\n  discount NUMERIC(5,2)  CHECK (discount BETWEEN 0 AND 100),\n  status   TEXT NOT NULL CHECK (status IN ('draft', 'active', 'archived'))\n);\n\n-- Cross-column check\nALTER TABLE bookings\n  ADD CONSTRAINT chk_dates CHECK (check_out > check_in);\n```\n\nLimitations: `CHECK` expressions cannot reference other tables (use triggers\nor application logic for cross-table validation). Some databases (MySQL pre-8.0)\nparsed but silently ignored `CHECK` constraints.\n\n**Rule of thumb:** use `CHECK` to enforce domain rules on a single row\n(non-negative price, valid status enum, date ordering). Prefer an `ENUM` type\nor a FK to a lookup table when the valid set of values is managed by\nthe business and may change.\n",{"id":49,"difficulty":25,"q":50,"a":51},"not-null-constraint","What does NOT NULL enforce and when should you omit it?","`NOT NULL` prevents the column from storing a `NULL`. An attempt to insert\nor update a row with `NULL` in that column raises an error.\n\n```sql\nCREATE TABLE events (\n  id          BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n  name        TEXT    NOT NULL,            -- must always be present\n  description TEXT,                        -- optional (nullable)\n  occurred_at TIMESTAMPTZ NOT NULL DEFAULT now()\n);\n```\n\nAdd `NOT NULL` when:\n- The value is required for every row to be meaningful.\n- You need aggregate functions (like `SUM`, `AVG`) to behave predictably\n  (`NULL` is skipped by aggregates).\n\nOmit `NOT NULL` (allow `NULL`) when:\n- The value is genuinely optional (\"middle name\", \"description\").\n- You want to distinguish \"not yet provided\" from a default value.\n\n**Rule of thumb:** default to `NOT NULL`; make a column nullable only when\nthe absence of a value has a distinct meaning you intend to query for.\n",{"id":53,"difficulty":54,"q":55,"a":56},"deferrable-constraints","hard","What are deferrable constraints and when do you need them?","By default, constraints are checked **immediately** after each statement.\nA **deferrable constraint** can be postponed until `COMMIT`, which is\nnecessary when two statements in the same transaction temporarily violate\nthe constraint before reaching a consistent state.\n\n```sql\n-- Postgres: declare the FK as deferrable\nALTER TABLE nodes\n  ADD CONSTRAINT fk_parent\n  FOREIGN KEY (parent_id) REFERENCES nodes(id)\n  DEFERRABLE INITIALLY DEFERRED;\n\n-- Now you can insert in any order within a transaction\nBEGIN;\n  INSERT INTO nodes (id, parent_id) VALUES (2, 1);  -- parent 1 doesn't exist yet\n  INSERT INTO nodes (id, parent_id) VALUES (1, NULL);\nCOMMIT;  -- FK checked here — both rows now exist, so it passes\n```\n\nCommon use case: self-referential trees, circular FK graphs, or bulk\nimports where you cannot guarantee insert order.\n\n**Rule of thumb:** use `DEFERRABLE INITIALLY DEFERRED` for FK constraints\nin self-referential or circular relationship tables. Leave constraints\n`NOT DEFERRABLE` by default — immediate checking catches bugs faster.\n",{"id":58,"difficulty":54,"q":59,"a":60},"exclusion-constraint","What is an exclusion constraint in Postgres?","An **exclusion constraint** (Postgres-specific) generalizes `UNIQUE` to\narbitrary operators, not just equality. It ensures that for any two rows,\nat least one of the specified conditions is false. The classic use case is\npreventing **overlapping time ranges**.\n\n```sql\n-- Requires btree_gist extension for mixed operator support\nCREATE EXTENSION IF NOT EXISTS btree_gist;\n\nCREATE TABLE room_bookings (\n  room_id    INT  NOT NULL,\n  reserved   TSTZRANGE NOT NULL,  -- time range type\n  EXCLUDE USING GIST (\n    room_id  WITH =,              -- same room\n    reserved WITH &&              -- overlapping periods\n  )\n);\n\n-- This pair of inserts will succeed\nINSERT INTO room_bookings VALUES (1, '[2026-06-01, 2026-06-03)');\nINSERT INTO room_bookings VALUES (1, '[2026-06-05, 2026-06-07)');\n\n-- This overlaps and will fail the exclusion constraint\nINSERT INTO room_bookings VALUES (1, '[2026-06-02, 2026-06-06)');\n```\n\n**Rule of thumb:** use exclusion constraints for scheduling, resource\nallocation, or any domain where overlapping intervals must be prevented.\nThey are more expressive than triggers for this pattern.\n",{"id":62,"difficulty":14,"q":63,"a":64},"naming-constraints","Why should you name your constraints explicitly?","When you omit a name, the database generates one automatically\n(`orders_customer_id_fkey` in Postgres, a UUID-based name in SQL Server).\nUnnamed constraints are hard to reference in migrations, error messages, and\napplication code.\n\n```sql\n-- Anonymous (avoid)\nALTER TABLE orders ADD FOREIGN KEY (customer_id) REFERENCES customers(id);\n\n-- Named (preferred)\nALTER TABLE orders\n  ADD CONSTRAINT fk_orders_customer\n  FOREIGN KEY (customer_id) REFERENCES customers(id);\n\n-- Now you can drop it cleanly\nALTER TABLE orders DROP CONSTRAINT fk_orders_customer;\n```\n\nA consistent naming convention (`fk_\u003Ctable>_\u003Ccol>`, `uq_\u003Ctable>_\u003Ccol>`,\n`chk_\u003Ctable>_\u003Crule>`) makes the schema self-documenting and makes migration\nscripts reliable across environments.\n\n**Rule of thumb:** always name constraints explicitly with a predictable\nconvention. Anonymous constraints force you to query the system catalog every\ntime you need to alter or drop one.\n",{"id":66,"difficulty":54,"q":67,"a":68},"partial-unique-index","What is a partial unique index and when does it replace a constraint?","A **partial unique index** applies the uniqueness guarantee only to rows\nthat satisfy a `WHERE` condition. This is useful when uniqueness should apply\nonly to a subset of rows — for example, only *active* records.\n\n```sql\n-- Only one active record per user (soft-delete pattern)\nCREATE UNIQUE INDEX uq_subscriptions_active_user\n  ON subscriptions (user_id)\n  WHERE cancelled_at IS NULL;\n\n-- Two rows for user 1, one cancelled and one active — both allowed\nINSERT INTO subscriptions (user_id, cancelled_at) VALUES (1, '2025-01-01');\nINSERT INTO subscriptions (user_id, cancelled_at) VALUES (1, NULL);  -- OK\n\n-- A second active row for user 1 — fails\nINSERT INTO subscriptions (user_id, cancelled_at) VALUES (1, NULL);  -- ERROR\n```\n\n**Rule of thumb:** use a partial unique index when the uniqueness rule\napplies to a subset of rows (e.g., non-deleted, active-status). Full `UNIQUE`\nconstraints cannot express this; partial indexes can.\n",{"id":70,"difficulty":14,"q":71,"a":72},"disable-enable-constraint","Can you disable a constraint temporarily and should you?","In **SQL Server** you can `DISABLE` a foreign key or check constraint and\nlater `ENABLE` it, optionally with `WITH NOCHECK` (skip validation of\nexisting data) or `WITH CHECK` (validate existing data on re-enable).\n\nIn **Postgres**, you can `SET CONSTRAINTS ALL DEFERRED` for the current\ntransaction, or `ALTER TABLE … DISABLE TRIGGER ALL` to disable trigger-based\nconstraints. You can also drop and recreate constraints for bulk loads.\n\nIn **MySQL**, `SET FOREIGN_KEY_CHECKS = 0` disables FK checks session-wide.\n\n```sql\n-- SQL Server bulk load workaround\nALTER TABLE orders NOCHECK CONSTRAINT fk_orders_customer;\nBULK INSERT orders FROM 'orders.csv';\nALTER TABLE orders WITH CHECK CHECK CONSTRAINT fk_orders_customer;\n\n-- MySQL bulk load\nSET FOREIGN_KEY_CHECKS = 0;\nLOAD DATA INFILE 'orders.csv' INTO TABLE orders;\nSET FOREIGN_KEY_CHECKS = 1;\n```\n\n**Rule of thumb:** disabling constraints is acceptable for controlled bulk\nloads, but always re-enable and validate immediately. Never disable\nconstraints permanently — you will eventually have corrupt data.\n",{"id":74,"difficulty":14,"q":75,"a":76},"index-vs-constraint","How does a UNIQUE constraint relate to a unique index?","A `UNIQUE` constraint is enforced by a **unique index** under the hood.\nIn Postgres and SQL Server, a `UNIQUE` constraint and a `CREATE UNIQUE INDEX`\non the same column are nearly equivalent — the constraint simply gives the\nindex a constraint-associated name.\n\n```sql\n-- These two are functionally equivalent in Postgres:\nALTER TABLE users ADD CONSTRAINT uq_users_email UNIQUE (email);\n\nCREATE UNIQUE INDEX uq_users_email ON users (email);\n```\n\nDifferences in Postgres:\n- Constraints can be `DEFERRABLE`; indexes cannot.\n- You can `DROP CONSTRAINT` but must `DROP INDEX` for a standalone index.\n- Partial unique indexes cannot be declared as a `UNIQUE` constraint\n  (you need the `CREATE UNIQUE INDEX … WHERE` form).\n\n**Rule of thumb:** use the `CONSTRAINT … UNIQUE` form when you need\ndeferred checking or want the DDL to be clearly declarative. Use\n`CREATE UNIQUE INDEX` when you need a partial unique condition.\n",{"id":78,"difficulty":25,"q":79,"a":80},"constraint-violations-errors","What error do you get when a constraint is violated, and how do you handle it?","Each constraint violation raises a specific error that application code can\ncatch and present meaningfully:\n\n| Constraint | Postgres SQLSTATE | Typical message |\n|---|---|---|\n| NOT NULL | 23502 | null value in column \"x\" violates not-null constraint |\n| UNIQUE | 23505 | duplicate key value violates unique constraint \"uq_…\" |\n| FOREIGN KEY | 23503 | insert or update on table \"x\" violates foreign key constraint |\n| CHECK | 23514 | new row for relation \"x\" violates check constraint \"chk_…\" |\n\n```python\n# Python + psycopg2 example\nfrom psycopg2 import errors\ntry:\n    cur.execute(\"INSERT INTO users (email) VALUES (%s)\", (email,))\nexcept errors.UniqueViolation:\n    return {\"error\": \"That email is already registered\"}\n```\n\n**Rule of thumb:** catch constraint violations by **SQLSTATE code**, not by\nparsing the error message string — message text can change between database\nversions. Map each violation to a user-friendly message in the application layer.\n",{"id":82,"difficulty":25,"q":83,"a":84},"table-vs-column-constraint","What is the difference between a column-level and a table-level constraint?","A **column-level constraint** is declared inline with the column definition\nand can only reference that single column. A **table-level constraint** is\ndeclared separately (after all column definitions) and can reference multiple\ncolumns.\n\n```sql\nCREATE TABLE order_items (\n  order_id   INT NOT NULL,               -- column-level NOT NULL\n  product_id INT NOT NULL,               -- column-level NOT NULL\n  quantity   INT NOT NULL CHECK (quantity > 0),  -- column-level CHECK\n\n  -- Table-level: composite PK (must reference both columns — impossible column-level)\n  PRIMARY KEY (order_id, product_id),\n\n  -- Table-level: cross-column check\n  CONSTRAINT chk_valid_quantity CHECK (quantity \u003C= 1000 OR order_id \u003C 1000)\n);\n```\n\n**Rule of thumb:** use column-level constraints for single-column rules\n(NOT NULL, UNIQUE, CHECK on one column). Use table-level constraints\nfor composite keys, composite unique indexes, and cross-column CHECK\nexpressions.\n",15,null,{"description":11},"SQL constraints interview questions — PRIMARY KEY, FOREIGN KEY, UNIQUE, CHECK, NOT NULL, referential actions, deferrable constraints, and constraint best practices across Postgres, MySQL, and SQL Server.","sql\u002Fschema\u002Fconstraints","Constraints & Integrity","Schema & Data Types","schema","2026-06-20","rq8jkLiLHCb4HPydx8hdz0jqAzhIQYLbWY0jcAKimbE",[96,100,103,104],{"subtopic":97,"path":98,"order":99},"Data Types","\u002Fsql\u002Fschema\u002Fdata-types",1,{"subtopic":101,"path":102,"order":12},"DDL — Creating & Altering Tables","\u002Fsql\u002Fschema\u002Fddl",{"subtopic":90,"path":21,"order":20},{"subtopic":105,"path":106,"order":107},"Normalization","\u002Fsql\u002Fschema\u002Fnormalization",4,{"path":109,"title":110},"\u002Fblog\u002Fsql-constraints-primary-foreign-unique","SQL Constraints — PRIMARY KEY, FOREIGN KEY, UNIQUE, CHECK, and NOT NULL",1782244107151]