Перейти к содержимому

Проектирование таблиц: типы и ограничения

Курс MS SQL с нуля · Урок 8 из 12 · Часть 3. Изменение и проектирование

Урок отвечает на вопрос «как устроена учебная база изнутри» и учит проектировать свои таблицы: выбор типов, смысл NULL, ключи и ограничения. Это последний урок блока «данные» — дальше вычисления и представления.

Урок создаёт две учебные таблицы рядом со схемой TechStore; в конце занятия они убираются DROP, база остаётся девственной.

Содержание

1. Что вы освоите

К концу урока вы:

  • выбираете тип под смысл поля: деньги в DECIMAL, дата-время в DATETIME2;
  • решаете допуск NULL по предметной области, а не по привычке;
  • ставите PRIMARY KEY, FOREIGN KEY, CHECK, UNIQUE и DEFAULT с именами;
  • читаете Msg 547 по имени ограничения и отличаете FK от CK;
  • проверяете замысел по метаданным sys.columns и sys.check_constraints.
↑ К оглавлению

2. Числа, строки и даты

ТипНазначениеПравило курса
INT и BIGINTцелые числа и идентификаторыBIGINT, когда значения за миллиарды
DECIMAL(p, s)деньги, веса, точные дробиприближённый FLOAT для денег запрещён
NVARCHAR(n)строки Unicodeлитералы с префиксом N
DATETIME2(0)дата и время до секундыDATETIME старого образца новым таблицам не нужен
DATEтолько датакалендарные сущности без времени
BITфлаги вида IsActiveNULL в флагах почти не нужен
Деньги во FLOAT: приближённая арифметика даёт расхождение итогов на копейках, который не видно в первые месяцы. Единственный тип курса для денег — DECIMAL.
Выбор длины строк: NVARCHAR(255) вслепую — не проектирование: длину подбирают по предметной области (имя двести знаков, телефон тридцать два с запасом), а не по «круглому» числу.
↑ К оглавлению

3. NULL как контракт

NULL — контракт «значения может не быть». Правила проектирования курса просты:

  • колонка допускает NULL, только если в предметной области существует смысл «неизвестно» (Email у клиента может отсутствовать честно);
  • NOT NULL с DEFAULT — частая пара для системных полей: дата создания заполняется сама;
  • выбор NULL или умолчания делается по смыслу, а не «чтобы вставка не падала».
↑ К оглавлению

4. Ключи и ограничения

ОграничениеЧто гарантируетИмя по канону курса
PRIMARY KEYуникальный непустой номер строкиPK_Таблица
IDENTITY(1,1)автоинкремент номерасвойство столбца
FOREIGN KEYсуществование родителя; Msg 547 при нарушенииFK_Таблица_Куда
UNIQUEальтернативный уникальный ключUQ_Таблица_Столбец
CHECKобласть допустимых значенийCK_Таблица_Смысл
DEFAULTзначение по умолчанию при вставкеDF_Таблица_Столбец

Чистый код

CREATE TABLE dbo.ProductReviews
(
    ReviewID    INT IDENTITY(1,1) NOT NULL,
    ProductID   INT               NOT NULL,
    CustomerID  INT               NOT NULL,
    Rating      TINYINT           NOT NULL,
    ReviewText  NVARCHAR(2000)    NULL,
    CreatedAt   DATETIME2(0)      NOT NULL DEFAULT SYSDATETIME(),
    CONSTRAINT PK_ProductReviews PRIMARY KEY (ReviewID),
    CONSTRAINT FK_ProductReviews_Product
        FOREIGN KEY (ProductID) REFERENCES dbo.Products (ProductID),
    CONSTRAINT FK_ProductReviews_Customer
        FOREIGN KEY (CustomerID) REFERENCES dbo.Customers (CustomerID),
    CONSTRAINT CK_ProductReviews_Rating CHECK (Rating BETWEEN 1 AND 5)
);
GO
SQL
  • Построчный разбор

    Ниже приведён тот же фрагмент с пояснением после каждой смысловой строки. Строки, которые начинаются с двух дефисов и стрелки вправо, содержат комментарий преподавателя. Эти строки не являются обязательной частью программы.

    CREATE TABLE dbo.ProductReviews
    -- → таблица отзывов появляется одной командой
    (
        ReviewID    INT IDENTITY(1,1) NOT NULL,
    -- → ключ с автошагом: сервер сам нумерует от единицы
        ProductID   INT               NOT NULL,
        CustomerID  INT               NOT NULL,
    -- → обе ссылки обязательны: сирот отзыву существовать не положено
        Rating      TINYINT           NOT NULL,
    -- → оценки 1-5: TINYINT хранит до 255 с тремя байтами экономии против INT
        ReviewText  NVARCHAR(2000)    NULL,
    -- → текст допускает отсутствие: NULL как честный контракт
        CreatedAt   DATETIME2(0)      NOT NULL DEFAULT SYSDATETIME(),
    -- → момент ставится сервером, если столбец при вставке не назван
        CONSTRAINT PK_ProductReviews PRIMARY KEY (ReviewID),
    -- → первичный ключ назван по канону PK плюс таблица
        CONSTRAINT FK_ProductReviews_Product
            FOREIGN KEY (ProductID) REFERENCES dbo.Products (ProductID),
        CONSTRAINT FK_ProductReviews_Customer
            FOREIGN KEY (CustomerID) REFERENCES dbo.Customers (CustomerID),
    -- → два внешних ключа: Msg 547 научится блокировать сирот по именам
        CONSTRAINT CK_ProductReviews_Rating CHECK (Rating BETWEEN 1 AND 5)
    -- → коридор оценок проверяется самой базой
    );
    GO
    SQL
CHECK пропускает NULL: проверка CHECK отсекает только явную ложь: NULL она не считает нарушением. «Оценка обязательна» обеспечивается парой NOT NULL плюс CHECK из таблицы выше.
↑ К оглавлению

5. Задачи и эталонные решения

Шесть задач на контексте TechStore. Рабочие таблицы создаются под собой и убираются DROP в конце занятия.

  1. спроектировать dbo.ProductReviews по карточке выше: ключи, CHECK, умолчание даты;
  2. вставить три отзыва на разные товары без упоминания IDENTITY;
  3. сломать защиту дважды: оценкой вне диапазона и ссылкой на несуществующий товар; записать номера ошибок;
  4. самопроверка по метаданным: столбцы из sys.columns, ограничения из sys.check_constraints и sys.foreign_keys;
  5. таблица правил скидок dbo.DiscountRules: процент CHECK от единицы до пятидесяти, признак активности DEFAULT 1, три правила;
  6. чтение своих ограничений по именам в sys.objects, сортировка по типу.

Эталон самопроверки

метаданные таблицы

Чистый код

SELECT c.name AS column_name, t.name AS type_name, c.max_length, c.is_nullable
FROM sys.columns AS c
JOIN sys.tables  AS t ON t.object_id = c.object_id
WHERE t.name = N'ProductReviews'
ORDER BY c.column_id;

SELECT cc.name AS check_name, cc.definition
FROM sys.check_constraints AS cc
JOIN sys.tables AS t ON t.object_id = cc.parent_object_id
WHERE t.name = N'ProductReviews';
GO
сверка замысла с реальностью
  • Построчный разбор

    Ниже приведён тот же фрагмент с пояснением после каждой смысловой строки. Строки, которые начинаются с двух дефисов и стрелки вправо, содержат комментарий преподавателя. Эти строки не являются обязательной частью программы.

    SELECT c.name AS column_name, t.name AS type_name, c.max_length, c.is_nullable
    -- → столбцы таблицы: имена, типы, длина и NULL-допуск
    FROM sys.columns AS c
    JOIN sys.tables  AS t ON t.object_id = c.object_id
    -- → каталог метаданных: связываем столбцы с их таблицей
    WHERE t.name = N'ProductReviews'
    ORDER BY c.column_id;
    -- → в порядке объявления при создании
    
    SELECT cc.name AS check_name, cc.definition
    -- → второй запрос: проверочные ограничения с определением коридора
    FROM sys.check_constraints AS cc
    JOIN sys.tables AS t ON t.object_id = cc.parent_object_id
    WHERE t.name = N'ProductReviews';
    -- → сверка замысла с реальностью: сервер показывает то, что реально записано
    GO
    сверка замысла с реальностью
↑ К оглавлению

6. Типичные ошибки урока

  1. Деньги в FLOAT — копеечные расхождения; DECIMAL только.
  2. NOT NULL без DEFAULT в системном поле — вставка без столбца падает с Msg 515; либо NULL допускаем, либо умолчание даём.
  3. IDENTITY «истёк» — слух; реальная задача — осознанный IDENTITY_INSERT при вставке явных номеров, а не паника.
  4. FK с обеих сторон — ссылку ставит дочерняя таблица; два встречных FK означают циклическое проектирование.
  5. CHECK затыкает NULL мысленно — на деле пропускает; полная защита дополняется NOT NULL.
  6. Безымянные ограничения — система сгенерирует имена вида CK__таблица__6329663811901420454; Msg 547 по такому имени не читается, поэтому курс именует всё сам.
↑ К оглавлению

7. Чек-лист практики и источники

Чек-лист урока 8

отметки делаются по факту

Источники для углубления:

  1. CREATE TABLE: полный синтаксис;
  2. Типы данных: точность и диапазоны;
  3. UNIQUE и CHECK: гарантии ограничений.
Итог: таблица проектируется с защитой на уровне схемы. Урок 9 — вычисления в запросах: строковые функции, даты, CASE и COALESCE.

Проверено: 23.10.2026

↑ К оглавлению
Конспект:
Четверг, 22 октября 2026