DDL - Зміна та видалення таблиць (ALTER, DROP)
DDL - Зміна та видалення таблиць (ALTER, DROP)
Навіщо змінювати існуючі таблиці?
У реальних проектах вимоги змінюються з часом:
Типові сценарії:
- Додавання нових функцій (нові стовпці)
- Виправлення помилок проектування
- Оптимізація performance
- Видалення застарілих структур
ALTER TABLE: Зміна структури таблиці
ALTER TABLE — це потужна команда, яка дозволяє змінювати існуючі таблиці без втрати даних.
Загальний синтаксис
ALTER TABLE table_name
{
ADD column_definition |
ALTER COLUMN column_name new_data_type |
DROP COLUMN column_name |
ADD CONSTRAINT constraint_definition |
DROP CONSTRAINT constraint_name
};
ADD COLUMN: Додавання стовпців
Базовий синтаксис
ALTER TABLE table_name
ADD column_name data_type [constraints];
Приклад 1: Додавання простого стовпця
-- Додаємо стовпець PhoneNumber до таблиці Students
ALTER TABLE Students
ADD PhoneNumber NVARCHAR(20) NULL;
Що відбувається:
- SQL Server додає новий стовпець
PhoneNumberдо таблиці - Для всіх існуючих записів значення цього стовпця буде
NULL - Структура таблиці змінюється, але дані зберігаються
Перевірка:
-- Подивимося на оновлену структуру
SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Students';
Приклад 2: Додавання стовпця з DEFAULT
Якщо додаємо NOT NULL стовпець до таблиці з даними, потрібно вказати DEFAULT:
-- ❌ ПОМИЛКА - NOT NULL без DEFAULT для існуючих записів
ALTER TABLE Students
ADD IsActive BIT NOT NULL;
-- Msg 515: Cannot insert NULL into column 'IsActive'
-- ✅ ПРАВИЛЬНО - з DEFAULT значенням
ALTER TABLE Students
ADD IsActive BIT NOT NULL DEFAULT 1;
Пояснення:
- Існуючі записи отримають значення
1(за замовчуванням) - Нові записи також матимуть
1, якщо не вказано інше
Приклад 3: Додавання обчислюваного стовпця
-- Додаємо обчислюваний стовпець Age (вік на основі дати народження)
ALTER TABLE Students
ADD Age AS (DATEDIFF(YEAR, BirthDate, GETDATE())) PERSISTED;
Додавання кількох стовпців одночасно
-- Додаємо декілька стовпців за один раз
ALTER TABLE Students
ADD
PhoneNumber NVARCHAR(20) NULL,
Address NVARCHAR(200) NULL,
City NVARCHAR(50) NULL,
PostalCode NVARCHAR(10) NULL;
ALTER COLUMN: Зміна типу даних
Базовий синтаксис
ALTER TABLE table_name
ALTER COLUMN column_name new_data_type [NULL | NOT NULL];
Приклад 1: Збільшення розміру текстового поля
-- Збільшуємо розмір FirstName з 50 до 100 символів
ALTER TABLE Students
ALTER COLUMN FirstName NVARCHAR(100) NOT NULL;
Приклад 2: Зменшення розміру (НЕБЕЗПЕЧНО!)
-- ⚠️ НЕБЕЗПЕЧНО - зменшуємо розмір Email з 100 до 50
ALTER TABLE Students
ALTER COLUMN Email NVARCHAR(50) NULL;
-- Msg 8152: String or binary data would be truncated
- Перевірте максимальну довжину ПЕРЕД зміною:
SELECT MAX(LEN(Email)) AS MaxEmailLength FROM Students; - Якщо потрібно, очистіть/скоротіть дані спочатку
Приклад 3: Зміна типу даних з конверсією
-- Припустимо, Age зараз NVARCHAR (помилка проектування)
-- Хочемо змінити на INT
-- Крок 1: Перевіряємо, чи всі значення можна конвертувати
SELECT Age
FROM Students
WHERE ISNUMERIC(Age) = 0; -- Знайти некоректні значення
-- Крок 2: Виправляємо некоректні значення (якщо є)
UPDATE Students
SET Age = NULL
WHERE ISNUMERIC(Age) = 0;
-- Крок 3: Змінюємо тип
ALTER TABLE Students
ALTER COLUMN Age INT NULL;
Обмеження при зміні типу
НЕ МОЖНА змінити тип, якщо:
DROP COLUMN: Видалення стовпців
Базовий синтаксис
ALTER TABLE table_name
DROP COLUMN column_name;
Приклад 1: Видалення одного стовпця
-- Видаляємо стовпець PhoneNumber
ALTER TABLE Students
DROP COLUMN PhoneNumber;
Приклад 2: Видалення кількох стовпців
-- Видаляємо кілька стовпців одночасно
ALTER TABLE Students
DROP COLUMN Address, City, PostalCode;
Обмеження при видаленні
НЕ МОЖНА видалити стовпець, якщо:
-- ❌ Стовпець є PRIMARY KEY
ALTER TABLE Students
DROP COLUMN Id;
-- Msg 5074: The object 'PK_Students' is dependent on column 'Id'
-- ❌ Стовпець є FOREIGN KEY
ALTER TABLE Enrollments
DROP COLUMN StudentId;
-- Msg 5074: The object 'FK_Enrollments_Students' is dependent on column 'StudentId'
-- ❌ Стовпець має індекс
-- ❌ Стовпець використовується в computed column
-- ❌ Стовпець має constraint
Розв'язок: Спочатку видаліть залежності, потім стовпець:
-- Крок 1: Видалити constraint
ALTER TABLE Students
DROP CONSTRAINT UQ_Students_Email;
-- Крок 2: Видалити стовпець
ALTER TABLE Students
DROP COLUMN Email;
ADD CONSTRAINT: Додавання обмежень
Ви можете додавати constraints до існуючих таблиць.
PRIMARY KEY
-- Додаємо PRIMARY KEY до існуючої таблиці
ALTER TABLE Products
ADD CONSTRAINT PK_Products PRIMARY KEY (ProductId);
FOREIGN KEY
-- Додаємо зв'язок між Enrollments та Students
ALTER TABLE Enrollments
ADD CONSTRAINT FK_Enrollments_Students
FOREIGN KEY (StudentId) REFERENCES Students(Id)
ON DELETE CASCADE
ON UPDATE CASCADE;
Що перевіряється при додаванні FK:
- Всі значення
StudentIdвEnrollmentsмають існувати вStudents.Id - Якщо є "orphan" записи (посилання на неіснуючі ID), операція не виконається
Приклад помилки:
-- Якщо є StudentId = 999, але такого студента немає
ALTER TABLE Enrollments
ADD CONSTRAINT FK_Enrollments_Students...
-- ❌ Msg 547: The ALTER TABLE statement conflicted with the FOREIGN KEY constraint
Виправлення:
-- Знайти orphan записи
SELECT DISTINCT StudentId
FROM Enrollments
WHERE StudentId NOT IN (SELECT Id FROM Students);
-- Видалити або виправити їх
DELETE FROM Enrollments WHERE StudentId NOT IN (SELECT Id FROM Students);
UNIQUE
-- Додаємо унікальність Email
ALTER TABLE Students
ADD CONSTRAINT UQ_Students_Email UNIQUE (Email);
CHECK
-- Додаємо валідацію для віку
ALTER TABLE Students
ADD CONSTRAINT CK_Students_Age CHECK (Age >= 16 AND Age <= 100);
-- Додаємо валідацію для грантів
ALTER TABLE Students
ADD CONSTRAINT CK_Students_Grants CHECK (Grants >= 0);
DEFAULT
-- Додаємо значення за замовчуванням
ALTER TABLE Students
ADD CONSTRAINT DF_Students_IsActive DEFAULT 1 FOR IsActive;
З WITH VALUES (застосувати до існуючих NULL записів):
-- Застосувати DEFAULT до існуючих записів з NULL
ALTER TABLE Students
ADD CONSTRAINT DF_Students_Country DEFAULT 'Ukraine' FOR Country WITH VALUES;
DROP CONSTRAINT: Видалення обмежень
Базовий синтаксис
ALTER TABLE table_name
DROP CONSTRAINT constraint_name;
Приклади
-- Видалити PRIMARY KEY
ALTER TABLE Products
DROP CONSTRAINT PK_Products;
-- Видалити FOREIGN KEY
ALTER TABLE Enrollments
DROP CONSTRAINT FK_Enrollments_Students;
-- Видалити UNIQUE
ALTER TABLE Students
DROP CONSTRAINT UQ_Students_Email;
-- Видалити CHECK
ALTER TABLE Students
DROP CONSTRAINT CK_Students_Age;
-- Видалити DEFAULT
ALTER TABLE Students
DROP CONSTRAINT DF_Students_IsActive;
Як знайти назву constraint?
Якщо ви не пам'ятаєте назву constraint:
-- Знайти всі constraints для таблиці Students
SELECT
CONSTRAINT_NAME,
CONSTRAINT_TYPE
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE TABLE_NAME = 'Students';
-- Результат:
-- PK_Students PRIMARY KEY
-- FK_Enrollments_S... FOREIGN KEY
-- UQ_Students_Email UNIQUE
-- CK_Students_Age CHECK
Або для детальнішої інформації:
-- Детальна інформація про constraints
EXEC sp_helpconstraint 'Students';
Робота з індексами (короткий огляд)
Індекси - це окрема велика тема, але ось базові операції:
CREATE INDEX
-- Створити індекс на стовпці LastName для швидшого пошуку
CREATE INDEX IX_Students_LastName
ON Students(LastName);
-- Створити composite індекс
CREATE INDEX IX_Students_Name
ON Students(LastName, FirstName);
-- Створити UNIQUE індекс
CREATE UNIQUE INDEX IX_Students_Email
ON Students(Email);
DROP INDEX
-- Видалити індекс
DROP INDEX IX_Students_LastName ON Students;
DROP TABLE: Видалення таблиці
Базовий синтаксис
DROP TABLE table_name;
- Структуру таблиці
- ВСІ дані
- Всі індекси
- Всі constraints
- Всі triggers
Приклад
-- Видалити таблицю OldData
DROP TABLE OldData;
Проблема: Foreign Key блокує видалення
-- Спроба видалити Students
DROP TABLE Students;
-- ❌ Msg 3726: Could not drop object 'Students' because it is referenced by a FOREIGN KEY constraint
-- Розв'язок 1: Спочатку видалити дочірню таблицю
DROP TABLE Enrollments; -- Має FK на Students
DROP TABLE Students; -- Тепер можна
-- Розв'язок 2: Видалити FK constraint
ALTER TABLE Enrollments DROP CONSTRAINT FK_Enrollments_Students;
DROP TABLE Students;
DROP TABLE IF EXISTS (SQL Server 2016+)
Безпечна версія, яка не генерує помилку, якщо таблиці немає:
-- SQL Server 2016+
DROP TABLE IF EXISTS OldData;
-- Для старіших версій
IF OBJECT_ID('OldData', 'U') IS NOT NULL
BEGIN
DROP TABLE OldData;
END
Видалення кількох таблиць
-- Видалити кілька таблиць одночасно
DROP TABLE Table1, Table2, Table3;
TRUNCATE TABLE: Очищення даних
TRUNCATE TABLE — швидке видалення ВСІХ даних з таблиці, але зберігає структуру.
Синтаксис
TRUNCATE TABLE table_name;
Приклад
-- Видалити всі дані з таблиці Logs
TRUNCATE TABLE Logs;
-- Таблиця існує, але порожня
TRUNCATE vs DELETE
Характеристики:
- Дуже швидко (мінімальне логування)
- Видаляє ВСІ дані (не можна вказати WHERE)
- Скидає IDENTITY counter до початкового значення
- НЕ можна відкотити (rollback) в транзакції (в більшості випадків)
- Блокує всю таблицю
TRUNCATE TABLE Students;
-- Всі студенти видалені, наступний Id почнеться з 1
Характеристики:
- Повільніше (повне логування кожного рядка)
- Можна вказати WHERE для вибіркового видалення
- НЕ скидає IDENTITY counter
- Можна відкотити (rollback) в транзакції
- Блокує окремі рядки
DELETE FROM Students;
-- Всі студенти видалені, але наступний Id продовжить нумерацію
-- (якщо останній був 100, наступний буде 101)
Порівняльна таблиця
| Характеристика | TRUNCATE TABLE | DELETE (без WHERE) | DROP TABLE |
|---|---|---|---|
| Швидкість | Дуже швидко | Повільно | Швидко |
| Логування | Мінімальне | Повне (кожен рядок) | Мінімальне |
| WHERE clause | Ні | Так | N/A |
| IDENTITY reset | Так | Ні | Так |
| Rollback | Обмежено | Так | Обмежено |
| Структура таблиці | Зберігається | Зберігається | Видаляється |
| Constraints | Зберігаються | Зберігаються | Видаляються |
| Triggers | НЕ спрацьовують | Спрацьовують | НЕ спрацьовують |
| З FK references | Помилка | Можливо* | Помилка |
* Якщо є ON DELETE CASCADE або дочірні таблиці порожні
Коли використовувати TRUNCATE
-- Швидко очистити таблицю після тестів
TRUNCATE TABLE TestResults;
-- Підготувати таблицю до нового завантаження
TRUNCATE TABLE StagingData;
-- Почати нумерацію з 1 знову
TRUNCATE TABLE NewTable;
-- ПОМИЛКА - TRUNCATE не підтримує WHERE
TRUNCATE TABLE Students WHERE Age < 18;
-- Використовуйте DELETE
DELETE FROM Students WHERE Age < 18;
Практичні сценарії
Сценарій 1: Міграція схеми (додавання поля)
Завдання: Додати поле CreatedAt до існуючої таблиці Users.
-- Крок 1: Додати стовпець з DEFAULT для існуючих записів
ALTER TABLE Users
ADD CreatedAt DATETIME NOT NULL DEFAULT GETDATE();
-- Крок 2: Оновити DEFAULT для майбутніх вставок (опціонально)
-- За замовчуванням DEFAULT вже встановлено
-- Крок 3: Перевірка
SELECT TOP 5 UserId, Username, CreatedAt FROM Users;
Сценарій 2: Рефакторинг БД (розділення стовпця)
Завдання: Розділити поле FullName на FirstName та LastName.
-- Припустимо, FullName = "Петренко Іван"
-- Крок 1: Додати нові стовпці
ALTER TABLE Users
ADD
FirstName NVARCHAR(50) NULL,
LastName NVARCHAR(50) NULL;
-- Крок 2: Заповнити нові стовпці з існуючих даних
UPDATE Users
SET
LastName = LEFT(FullName, CHARINDEX(' ', FullName) - 1),
FirstName = SUBSTRING(FullName, CHARINDEX(' ', FullName) + 1, LEN(FullName));
-- Крок 3: Перевірити результат
SELECT FullName, FirstName, LastName FROM Users;
-- Крок 4: Зробити нові стовпці обов'язковими (якщо потрібно)
ALTER TABLE Users
ALTER COLUMN FirstName NVARCHAR(50) NOT NULL;
ALTER TABLE Users
ALTER COLUMN LastName NVARCHAR(50) NOT NULL;
-- Крок 5: Видалити старий стовпець (після підтвердження)
ALTER TABLE Users
DROP COLUMN FullName;
Сценарій 3: Додавання Foreign Key до існуючих таблиць
Завдання: Встановити зв'язок між Orders та Customers.
-- Крок 1: Перевірити orphan записи (замовлення без клієнтів)
SELECT DISTINCT CustomerId
FROM Orders
WHERE CustomerId NOT IN (SELECT CustomerId FROM Customers);
-- Крок 2: Виправити orphan записи
-- Варіант A: Видалити orphan records
DELETE FROM Orders
WHERE CustomerId NOT IN (SELECT CustomerId FROM Customers);
-- Варіант B: Встановити CustomerID в NULL або default значення
UPDATE Orders
SET CustomerId = NULL -- або якесь default значення
WHERE CustomerId NOT IN (SELECT CustomerId FROM Customers);
-- Крок 3: Додати Foreign Key
ALTER TABLE Orders
ADD CONSTRAINT FK_Orders_Customers
FOREIGN KEY (CustomerId) REFERENCES Customers(CustomerId)
ON DELETE NO ACTION;
-- Крок 4: Перевірка
EXEC sp_helpconstraint 'Orders';
Сценарій 4: Версіонування схеми БД
Профес approach до міграцій:
-- Файл: Migration_001_AddUserPreferences.sql
BEGIN TRANSACTION;
-- Перевірити, чи міграція вже виконана
IF NOT EXISTS (
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Users' AND COLUMN_NAME = 'PreferredLanguage'
)
BEGIN
PRINT 'Applying migration 001: Adding user preferences';
-- Додати стовпці
ALTER TABLE Users
ADD
PreferredLanguage NVARCHAR(10) DEFAULT 'uk' NOT NULL,
Theme NVARCHAR(20) DEFAULT 'light' NOT NULL,
NotificationsEnabled BIT DEFAULT 1 NOT NULL;
-- Створити індекс
CREATE INDEX IX_Users_Language ON Users(PreferredLanguage);
PRINT 'Migration 001 completed successfully';
END
ELSE
BEGIN
PRINT 'Migration 001 already applied, skipping';
END
COMMIT TRANSACTION;
Best Practices для ALTER TABLE
1. Завжди робіть BACKUP перед DDL
-- Перед виконанням ALTER на production
BACKUP DATABASE YourDatabase
TO DISK = 'C:\\Backups\\YourDB_BeforeAlter.bak';
2. Тестуйте на копії БД
Ніколи не запускайте DDL команди на production без тестування на dev/staging:
-- 1. Створити копію структури
SELECT * INTO Students_BACKUP FROM Students;
-- 2. Протестувати ALTER
ALTER TABLE Students_BACKUP ADD NewColumn INT;
-- 3. Якщо все добре - застосувати на Students
3. Використовуйте транзакції (де можливо)
BEGIN TRANSACTION;
ALTER TABLE Users ADD NewColumn INT;
-- Перевірити результат
SELECT TOP 5 * FROM Users;
-- Якщо все добре
COMMIT;
-- Якщо щось не так
-- ROLLBACK;
4. Додавайте стовпці з DEFAULT
Для NOT NULL стовпців завжди вказуйте DEFAULT:
-- ✅ Добре
ALTER TABLE Users
ADD CreatedAt DATETIME NOT NULL DEFAULT GETDATE();
-- ❌ Погано (помилка для існуючих записів)
ALTER TABLE Users
ADD CreatedAt DATETIME NOT NULL;
5. Перевіряйте залежності
Перед видаленням стовпців/constraints:
-- Знайти всі залежності стовпця
EXEC sp_depends 'Students.Email';
-- Знайти всі FK що посилаються на таблицю
SELECT
OBJECT_NAME(parent_object_id) AS ReferencingTable,
name AS ForeignKeyName
FROM sys.foreign_keys
WHERE referenced_object_id = OBJECT_ID('Students');
6. Документуйте зміни
Зберігайте всі DDL скрипти в системі контролю версій (Git):
migrations/
001_add_user_preferences.sql
002_refactor_full_name.sql
003_add_email_index.sql
Практичні завдання
Додайте до таблиці Students наступні поля:
Gender- стать ('M', 'F', 'Other'), NOT NULL з DEFAULT 'Other'EnrollmentDate- дата зарахування, NOT NULL з DEFAULT поточною датоюGPA- середній бал (0.00 - 5.00), NULL, з CHECK constraint
💡 Розв'язок
ALTER TABLE Students
ADD
Gender NVARCHAR(10) NOT NULL DEFAULT 'Other',
EnrollmentDate DATE NOT NULL DEFAULT GETDATE(),
GPA DECIMAL(3, 2) NULL;
-- Додати CHECK constraint
ALTER TABLE Students
ADD CONSTRAINT CK_Students_GPA CHECK (GPA BETWEEN 0.00 AND 5.00);
-- Додати CHECK для Gender
ALTER TABLE Students
ADD CONSTRAINT CK_Students_Gender CHECK (Gender IN ('M', 'F', 'Other'));
Збільшіть розмір поля Email в таблиці Students з 100 до 255 символів.
💡 Розв'язок
-- Спочатку перевіримо поточну максимальну довжину
SELECT MAX(LEN(Email)) AS MaxEmailLength FROM Students;
-- Збільшимо розмір
ALTER TABLE Students
ALTER COLUMN Email NVARCHAR(255) NULL;
-- Перевіримо зміну
SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Students' AND COLUMN_NAME = 'Email';
Видаліть поле Grants з таблиці Students, якщо воно існує. Але спочатку перевірте, чи немає залежностей.
💡 Розв'язок
-- Крок 1: Перевірити залежності
EXEC sp_depends 'Students.Grants';
-- Крок 2: Перевірити constraints
SELECT CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE
WHERE TABLE_NAME = 'Students' AND COLUMN_NAME = 'Grants';
-- Крок 3: Видалити constraint (якщо є)
-- ALTER TABLE Students DROP CONSTRAINT CK_Students_Grants;
-- Крок 4: Видалити стовпець
ALTER TABLE Students
DROP COLUMN Grants;
-- Крок 5: Перевірка
SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'Students';
Створіть таблицю Departments та додайте foreign key зв'язок з Students.
💡 Розв'язок
-- Крок 1: Створити таблицю Departments
CREATE TABLE Departments (
DepartmentId INT PRIMARY KEY IDENTITY(1, 1),
DepartmentName NVARCHAR(100) NOT NULL,
Faculty NVARCHAR(100) NOT NULL
);
-- Крок 2: Додати DepartmentId до Students
ALTER TABLE Students
ADD DepartmentId INT NULL;
-- Крок 3: Заповнити тестовими даними
INSERT INTO Departments (DepartmentName, Faculty)
VALUES
('Computer Science', 'Engineering'),
('Mathematics', 'Science');
-- Крок 4: Встановити DepartmentId для існуючих студентів
UPDATE Students SET DepartmentId = 1;
-- Крок 5: Зробити поле обов'язковим
ALTER TABLE Students
ALTER COLUMN DepartmentId INT NOT NULL;
-- Крок 6: Додати Foreign Key
ALTER TABLE Students
ADD CONSTRAINT FK_Students_Departments
FOREIGN KEY (DepartmentId) REFERENCES Departments(DepartmentId)
ON DELETE NO ACTION
ON UPDATE CASCADE;
-- Перевірка
EXEC sp_helpconstraint 'Students';
Резюме
- ADD COLUMN — додавання нових стовпців (з DEFAULT для NOT NULL)
- ALTER COLUMN — зміна типу даних (обережно з зменшенням розміру!)
- DROP COLUMN — видалення стовпців (незворотно, видаліть залежності спочатку)
- ADD/DROP CONSTRAINT — керування обмеженнями
- Повне видалення таблиці (структура + дані)
- Використовуйте
IF EXISTSдля безпеки - Foreign Keys блокують видалення
- Швидке очищення всіх даних
- Скидає IDENTITY counter
- Не підтримує WHERE
- Швидше за DELETE, але менш гнучке
- Завжди робіть backup перед DDL на production
- Тестуйте зміни на копії БД
- Документуйте всі міграції схеми
- Перевіряйте залежності перед видаленням
- Використовуйте версіонування для міграцій
Пов'язані теми: