В чем разница между пустой строкой и NULL в базе данных?

«В чем разница между пустой строкой и NULL в базе данных?» — вопрос из категории Базы данных, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Пустая строка ('') и NULL — это принципиально разные концепции в реляционных базах данных.

Пустая строка ('')

  • Что это: Значение строкового типа (VARCHAR, TEXT), которое представляет собой строку нулевой длины. Это валидное, известное значение.
  • Аналогия в мире объектов: string.Empty в C# или "" в Java.
  • Хранение: Занимает место в строке таблицы (для хранения информации о длине строки, которая равна 0).

NULL

  • Что это: Специальный маркер, означающий отсутствие какого-либо значения, «значение не известно», «не применимо» или «не указано». Это не строка, не число и не ноль.
  • Аналогия в мире объектов: null в C#/Java или None в Python.
  • Хранение: Обычно требует отдельного бита в строке для указания, что поле NULL. Само значение не хранится.

Практические различия и примеры SQL

-- Создадим тестовую таблицу
CREATE TABLE Users (
    Id INT PRIMARY KEY,
    Name VARCHAR(100),
    Nickname VARCHAR(100)
);

-- Вставим разные значения
INSERT INTO Users (Id, Name, Nickname) VALUES
(1, 'Alice', ''),        -- Nickname — пустая строка (пользователь сознательно оставил поле пустым)
(2, 'Bob', NULL),        -- Nickname — NULL (пользователь не заполнял поле вообще)
(3, 'Charlie', 'Chuck'); -- Nickname — обычное значение

-- КРИТИЧНО ВАЖНЫЕ РАЗЛИЧИЯ В ЗАПРОСАХ:

-- 1. Сравнение: для NULL используется специальный оператор IS
SELECT * FROM Users WHERE Nickname = '';   -- Найдет только Alice (Id=1)
SELECT * FROM Users WHERE Nickname IS NULL; -- Найдет только Bob (Id=2)
SELECT * FROM Users WHERE Nickname = NULL;  -- Этот запрос ВСЕГДА вернет пустой результат! (синтаксически верно, но логически не работает)

-- 2. Агрегатные функции игнорируют NULL
SELECT COUNT(Nickname) FROM Users; -- Вернет 2 (Alice и Charlie). NULL (Bob) проигнорирован.
SELECT AVG(LENGTH(Nickname)) FROM Users; -- Учтет только не-NULL значения (Alice=0, Charlie=5).

-- 3. Конкатенация с NULL дает NULL
SELECT Name || ' (' || Nickname || ')' FROM Users;
-- Результат: 'Alice ()', NULL (для Bob), 'Charlie (Chuck)'
-- Для Bob вся строка стала NULL, потому что один из операндов — NULL.
-- Чтобы избежать этого, используют COALESCE: Name || ' (' || COALESCE(Nickname, 'N/A') || ')'

Связь с кодом приложения (C# пример)

using (var reader = command.ExecuteReader())
{
    while (reader.Read())
    {
        // Правильное чтение из БД с учетом NULL
        string name = reader["Name"] as string; // Если в БД NULL, то в коде будет null
        object nicknameRaw = reader["Nickname"];

        // Проверка на DBNull (специальный тип для представления NULL из БД в .NET)
        string nickname;
        if (nicknameRaw == DBNull.Value)
        {
            nickname = null; // Значение было NULL в БД
        }
        else
        {
            nickname = (string)nicknameRaw; // Может быть "" или обычная строка
        }

        // Альтернатива — использовать reader.IsDBNull(columnIndex)
        // Или современные ORM (Entity Framework) делают это преобразование автоматически.
    }
}

Итог: Когда что использовать?

  • Используйте NULL, когда значение неизвестно, не применимо или не предоставлено (например, дата увольнения для действующего сотрудника).
  • Используйте пустую строку, когда значение известно и сознательно оставлено пустым (например, поле "Отчество", если его нет).

Важное замечание: В некоторых СУБД (например, Oracle) пустая строка ('') автоматически преобразуется в NULL. Это частый источник ошибок при миграции между базами данных. Всегда проверяйте поведение вашей целевой СУБД.