Какими средствами обеспечивается целостность данных в реляционных СУБД и как это реализуется в Go?

Ответ

Целостность данных в реляционных СУБД — это гарантия точности и согласованности данных. Она обеспечивается на нескольких уровнях:

  1. Ограничения (Constraints): Правила, заданные на уровне схемы данных.

    • PRIMARY KEY: Уникально идентифицирует каждую запись в таблице.
    • FOREIGN KEY: Связывает данные в одной таблице с данными в другой, обеспечивая ссылочную целостность.
    • UNIQUE: Гарантирует, что все значения в столбце или группе столбцов уникальны.
    • NOT NULL: Требует, чтобы столбец не содержал NULL значений.
    • CHECK: Проверяет, что значения в столбце соответствуют определенному условию.
    CREATE TABLE users (
        id SERIAL PRIMARY KEY,
        email VARCHAR(255) UNIQUE NOT NULL,
        age INT CHECK (age >= 18)
    );
    
    CREATE TABLE orders (
        id SERIAL PRIMARY KEY,
        user_id INT NOT NULL,
        -- При удалении пользователя, его заказы также будут удалены
        FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
    );
  2. Транзакции: Группа операций, которая выполняется как единое целое. Транзакции обладают свойствами ACID (Atomicity, Consistency, Isolation, Durability), которые гарантируют, что база данных переходит из одного согласованного состояния в другое.

    В Go транзакции управляются через пакет database/sql. Использование defer tx.Rollback() является идиоматичным и безопасным подходом.

    func transferMoney(db *sql.DB, fromID, toID int, amount float64) error {
        tx, err := db.Begin()
        if err != nil {
            return err
        }
        // defer tx.Rollback() гарантирует откат, если что-то пойдет не так.
        // Если tx.Commit() выполнится успешно, Rollback() вернет ошибку, которую можно проигнорировать.
        defer tx.Rollback()
    
        _, err = tx.Exec("UPDATE accounts SET balance = balance - $1 WHERE id = $2", amount, fromID)
        if err != nil {
            return err
        }
    
        _, err = tx.Exec("UPDATE accounts SET balance = balance + $1 WHERE id = $2", amount, toID)
        if err != nil {
            return err
        }
    
        // Если все операции успешны, фиксируем транзакцию
        return tx.Commit()
    }
  3. Нормализация: Процесс организации таблиц и столбцов для минимизации избыточности данных и улучшения их целостности.

  4. Триггеры: Хранимые процедуры, которые автоматически выполняются в ответ на определенные события (INSERT, UPDATE, DELETE) в таблице. Используются для реализации сложной бизнес-логики на уровне БД.

  5. Целостность на уровне приложения: Помимо механизмов БД, бизнес-логика в Go-приложении также должна проводить валидацию данных перед их записью в базу.