Из Google Sheet в реляционную схему: пошаговый план моделирования
Переход от Google Sheet к реляционной схеме: как найти повторяющиеся группы, выбрать ключи, смоделировать связи и избежать хаоса в данных.

Почему таблицы превращаются в хаос, когда их используют как базу данных
Таблица отлично подходит для небольшого списка. Можно быстро менять колонки, везде оставлять заметки и править ошибки на глаз. Такая свобода начинает ломаться, когда файл становится общим источником правды.
По мере роста данных одни и те же проблемы возникают снова и снова. Появляются дубликаты, потому что нет одного места для клиента или продукта. Появляются конфликтующие значения, когда разные строки дают разную информацию про одно и то же — например, разные номера телефонов. Фильтрация и отчёты становятся неудобными, потому что некоторые колонки прячут списки ("Теги", "Продукты", "Участники") или смешивают форматы ("$1,200", "1200", "1.2k").
Переход от Google Sheet к реляционной схеме — это про надёжность. База данных задаёт более явную структуру, чтобы вы могли запрашивать, валидировать и обновлять данные, не создавая новых противоречий.
Полезная мысленная модель: одна строка должна представлять одну реальную вещь. Если строка описывает сделку, клиента и список продуктов одновременно, любое изменение позже будет болезненным.
Быстрый тест: нужна ли одной строке два значения для одного и того же поля?
- Один заказ имеет несколько продуктов
- Один проект имеет несколько участников
- Один клиент имеет несколько адресов
Если ответ — да, это не проблема «широкой строки». Это проблема "отдельной таблицы". Как только вы смоделируете это чётко, можно строить формы и валидацию поверх модели, а не полагаться на хрупкие ручные правки.
Начните с определения, что именно означает таблица
Таблица может выглядеть организованной и при этом значить разное для разных людей. Прежде чем переводить Google Sheet в реляционную схему, договоритесь, что конкретно отслеживает таблица.
Начните с результатов, а не с колонок. Какие решения должны поддерживать данные: еженедельный отчёт по доходам, список просроченных тикетов, рабочий процесс для назначений, быстрый поиск во время звонка с клиентом? Если вы не можете назвать решение, поле часто не должно быть в базе.
Затем выделите существительные, скрытые в заголовках и заметках. Это обычно и становятся будущими таблицами: клиенты, заказы, продукты, счета, тикеты, агенты, локации. Если колонка смешивает два сущностных понятия (например, “Клиент + Компания”), вы храните несколько вещей в одном месте.
Рано договоритесь о значениях
Небольшие различия в понимании превращаются в большие правки позже. Проясните базовые вещи:
- Что считается «заказом» (коммерческое предложение, оплаченная покупка или то и другое)?
- Кто такой «клиент» (физлицо, компания или оба варианта)?
- Может ли один заказ включать несколько продуктов?
- Может ли один email принадлежать нескольким клиентам?
- Что показывает «статус» (текущее состояние или историю)?
Пример: если в таблице у вас одна строка на «Заказ», но ячейка «Продукты» содержит список, разделённый запятыми, решите, представляет ли строка оплату, отгрузку или счёт. Каждый выбор ведёт к разной схеме.
Сделайте копию исходной таблицы доступной только для чтения. Она пригодится, чтобы проверить, отвечают ли новые таблицы на те же вопросы.
Очистите таблицу, чтобы структура стала видна
Прежде чем переводить Google Sheet в реляционную схему, приведите таблицу к виду данных, а не отчёта. Базы данных требуют последовательных строк и колонок. Декоративная верстка прячет паттерны, которые нужно смоделировать.
Уберите трюки оформления: объединённые ячейки, несколько строк заголовков и промежуточные итоги внутри диапазона данных. Оставьте одну строку заголовков, затем только записи. Если нужны итоги — вынесите их на отдельную сводную вкладку, чтобы не смешивать с реальными записями.
Затем сделайте форматы единообразными по каждой колонке. База данных не догадается, что "1/2/24", "2024-02-01" и "Feb 1" — это одна и та же дата. То же касается телефонов, валют и имён. Выберите один формат и используйте его везде, даже если кажется строгим.
Короткий список правок, который обычно окупается:
- Убедитесь, что каждая строка представляет одну вещь (один заказ, одного клиента, один тикет).
- Удалите пустые строки и столбцы-разделители.
- Замените "N/A", "-" и пустые строки одним правилом, которое вы будете применять.
- Отметьте, какие колонки вычисляемые, а какие вводятся человеком.
Наконец, отметьте ячейки, содержащие несколько значений, например "красный, синий, зелёный" в одной колонке. Не меняйте схему немедленно — просто пометьте такие колонки, чтобы не забыть, что позже они станут отдельными строками.
Найдите повторяющиеся группы и поля, которые скрывают списки
Самый большой сигнал тревоги при моделировании данных из таблиц — повторение. Таблицы часто упаковывают «больше одного элемента» в одну строку, повторяя колонки или запихивая множество значений в одну ячейку. Это работает для быстрой учётности, но ломается, когда нужны фильтры, отчёты или согласованные обновления.
Шаблоны, которые обычно означают «это должна быть другая таблица»
Просканируйте таблицу в поисках таких форм:
- Нумерованные колонки, вроде
Item 1,Item 2,Item 3илиPhone 1,Phone 2. - Повторяющиеся блоки, например поля адреса, дублированные для «Дома» и «Работы».
- Ячейки с запятыми, переводами строки или словом "и", которые объединяют значения (например, "Mouse, Keyboard, Monitor").
- Колонка, смешивающая два понятия, как «Approved 2025-01-10» или "Alex (Manager)".
- Строка, которая одновременно представляет два уровня, например строка заказа, пытающаяся хранить и все позиции заказа.
Пример: если ваш трекер продаж использует Order ID, Customer, Product 1, Qty 1, Product 2, Qty 2, вы быстро упрётесь. У одних заказов 1 товар, у других 8. Таблица либо разрастётся вширь, либо начнёт терять данные. В реляционной модели "Orders" — отдельная таблица, а "Order Items" — таблица с одной строкой на товар в заказе.
Для «списков в ячейке» обращайтесь к каждому значению как к отдельной записи. Ячейка с текстом "Email, SMS" обычно означает, что нужна отдельная таблица (или join-таблица), чтобы отслеживать каналы корректно.
Тихие смешанные колонки могут быть так же опасны. Разделите их рано, чтобы каждое поле хранило одну понятную вещь.
Создайте таблицы из найденных сущностей
Как только вы выделите реальные вещи в таблице, превратите каждую в отдельную таблицу. Ваша таблица перестанет быть одной большой сеткой и станет набором небольших, целенаправленных списков.
Если строка смешивает данные о двух разных вещах, вероятно, нужны две таблицы. Строка в трекере продаж может включать информацию о клиенте (имя, телефон), информацию о заказе (дата, статус) и информацию о продукте (SKU, цена). Клиенты не меняются каждый раз, когда меняется заказ, а продукты не зависят от конкретного заказа. Разделение предотвращает дублирующие правки и несоответствия.
Прежде чем окончательно фиксировать структуру, напишите однострочное назначение для каждой таблицы. Если вы не можете описать, что означает таблица, не дописывая "и ещё", значит она слишком широкая.
Несколько практических правил:
- Держите атрибуты, которые описывают одну и ту же сущность и имеют один жизненный цикл, вместе (имя клиента и email клиента).
- Перенесите в отдельную таблицу всё, что может появляться несколько раз (несколько позиций заказа, несколько адресов).
- Если ячейка содержит список (значения, разделённые запятыми, повторяющиеся колонки), это отдельная таблица.
- Если два набора полей меняются по разным причинам, разделите их (статус заказа против контактной информации клиента).
Затем давайте колонкам понятные и последовательные имена. Предпочитайте простые существительные и избегайте расплывчатых ярлыков вроде "Info" или "Details".
Выбирайте ключи, которые остаются стабильными со временем
Раннее назначение первичного ключа для каждой таблицы — хорошая идея. Хороший ключ скучен: он никогда не меняется, всегда присутствует и однозначно идентифицирует строку.
Натуральные ключи (реальные значения) могут работать, но только если они действительно стабильны. SKU часто годится, потому что предназначен быть постоянным. Email кажется стабильным, но люди меняют почту, делят ящики и создают дубликаты вроде "john@" и "john.work@". Имена, телефоны и адреса меняются и не гарантируют уникальность.
Безопасный дефолт — авто-сгенерированный ID (например, customer_id, order_id). Храните натуральный идентификатор как обычное поле и добавляйте правило уникальности, когда это отвечает правилам вашего бизнеса. Если email изменится, customer_id останется тем же, и связанные заказы продолжат ссылаться на правильного клиента.
Простые правила по ключам:
- Используйте авто-ID, когда реальный идентификатор может меняться, отсутствовать или повторно использоваться.
- Используйте натуральный ключ только если вы контролируете его и он сделан постоянным (например, SKU).
- Отмечайте поля как уникальные только когда дубли действительно недопустимы.
- Разрешайте NULL только если состояние «неизвестно» валидно; иначе требуйте значение.
- Запишите, что именно значит «уникально» (уникально в таблице, в рамках компании или в пределах периода времени).
Пример: для таблицы Contacts используйте contact_id как первичный ключ. Оставьте email уникальным только если правило бизнеса — один контакт = один email. Разрешите phone быть пустым, потому что не у всех он есть.
Отображайте связи без догадок
Большинство серьёзных ошибок происходят из догадок о связях. Используйте простое правило: если одна строка «владеет» множеством чего-то, это один-ко-многим. Ставьте внешний ключ на стороне «многие».
Пример: один Customer может иметь много Orders. Таблица Orders должна хранить customer_id. Если вы храните в Customers список номеров заказов через запятую, быстро появятся дубли и пропущенные данные.
Many-to-many — частая ловушка таблиц. Если один Order может включать много Products и один Product может появляться во многих Orders, нужна join-таблица (обычно line items). В ней обычно order_id, product_id и поля вроде количества и цены на момент покупки.
Отношения один-к-одному встречаются редко. Они оправданы, когда дополнительные данные опциональны или вынесены по соображениям приватности или производительности (например, User и UserProfile). Если вы разделили таблицу только потому, что в таблице было две вкладки — это предупреждение.
История требует отдельной структуры. Если значения меняются со временем (статус, цена, адрес), избегайте перезаписи единственной колонки. Храните изменения как строки в таблице истории, чтобы можно было ответить «что было верно на ту дату?».
Нормализуйте достаточно, чтобы избежать противоречий
Правило попроще: храните один факт в одном месте. Если телефон клиента появляется в пяти строках, кто-то обновит четыре и пропустит пятую.
Нормализация простыми словами:
1NF, 2NF, 3NF на практике
Первая нормальная форма (1NF) значит, что каждая ячейка содержит одно значение. Если колонка содержит "красный, синий, зелёный" или "SKU1|SKU2|SKU3", это скрытый список. Разбейте это в строки в связанной таблице.
Вторая нормальная форма (2NF) часто проявляется в позициях заказа. Если у вас OrderItems с ключом (OrderID, ProductID), то такие поля как CustomerName туда не относятся — они зависят от заказа, а не продукта.
Третья нормальная форма (3NF) означает, что неключевые поля не должны зависеть от других неключевых полей. Если вы храните ZipCode и City, а City определяется ZipCode, вы рискуете несовпадениями.
Короткая самопроверка:
- Можно ли одно и то же значение отредактировать в нескольких местах?
- Вызовет ли одно изменение необходимость править много строк?
- Храните ли вы ярлыки, которые можно получить по ID?
- Хранятся ли итоги рядом с сырыми строками, из которых они вычислены?
Когда денормализация приемлема
Денормализуйте в основном для отчётности, когда много чтения, и делайте это безопасно: считайте отчетную таблицу копией, которую можно пересобрать. Держите нормализованные таблицы источником правды.
Для производных значений вроде итогов, балансов и статусов не дублируйте их без чёткого правила пересчёта. Практичный подход: храните сырые транзакции, вычисляйте итоги в запросах и кешируйте итоги только при необходимости ради производительности.
Частые ловушки моделирования, которые вызывают доработки
Большинство «в таблице работало» проблем вызвано смыслом, а не инструментами. Цель — чтобы каждая строка говорила одну ясную вещь одинаково каждый раз.
Распространённые ловушки:
- Использование имён как идентификаторов. "John Smith" не уникален и меняется. Используйте сгенерированный ID (или верифицированный email/телефон), а отображаемые имена храните как ярлыки.
- Упаковка списков в одну ячейку. Это выглядит просто, но ломает поиск, валидацию и отчёты. Списки должны быть в связанной таблице.
- Смешивание текущего состояния с историей. Одна колонка Status не может одновременно показывать текущее состояние и историю изменений. Если важно время — храните события со временем.
- Перегрузка таблицы для нескольких ролей. Лист Contacts, в котором есть клиенты, поставщики и сотрудники, обычно накапливает поля, применимые не ко всем строкам. Разделите по ролям или заведите общую таблицу Person и добавьте таблицы для ролей.
- Игнорирование обязательных и опциональных полей. Если ключевые поля могут быть пустыми, вы получите строки, которые нельзя корректно соединить. Решите заранее, что обязательно, и применяйте правила.
Если у таблицы Orders колонки Item 1, Item 2, Item 3, вы смотрите на повторяющуюся группу. Планируйте таблицу Orders и таблицу OrderItems.
Быстрый чек-лист перед фиксацией схемы
Прежде чем зафиксировать схему, сделайте финальный проход. Большая часть боли в базе появляется из небольших сокращений, которые казались безобидными.
Спросите себя, отвечает ли каждая таблица на один простой вопрос. "Customers" должно означать клиентов, а не клиентов плюс их последний заказ и заметки о звонках. Если вы не можете описать таблицу коротко — она смешивает понятия.
Последние проверки:
- Можете ли вы указать колонку (или набор колонок), которая уникально идентифицирует каждую строку, даже если имена поменяются?
- Есть ли ячейки с более чем одним значением (теги через запятую, несколько email, колонки Item1/Item2)? Если да — разделите в дочернюю таблицу.
- Для каждой связи — хранится ли она как намеренный внешний ключ? Для many-to-many есть ли join-таблица?
- Есть ли у важных полей правила (обязательно там, где пустые данные ломают процесс; уникально там, где дубли вредны)?
- Можно ли обновить факт (адрес клиента, цена продукта, роль сотрудника) в одном единственном месте?
Тест реальности: представьте, что кто-то вводит одного и того же клиента дважды с небольшим отличием в написании. Если ваша схема этому способствует, добавьте более строгий ключ или правило уникальности.
Пример: превращение трекера продаж в чистые таблицы
Представьте трекер продаж, где каждая строка — сделка. В колонках: Customer Name, Customer Email, Deal Amount, Stage, Close Date, Products (список, через запятую) и Notes (иногда несколько заметок в одной ячейке).
Эта строка скрывает две повторяющиеся группы: продукты (одна сделка может включать много продуктов) и заметки (у сделки может быть много заметок). Именно здесь часто происходят ошибки при конверсии, потому что списки внутри ячеек трудно запросить и легко получить противоречия.
Чистая модель после преобразования, соответствующая поведению работы:
- Customers (CustomerId, Name, Email)
- Deals (DealId, CustomerId, Amount, Stage, CloseDate)
- Products (ProductId, Name, SKU)
- DealProducts (DealId, ProductId, Quantity, UnitPrice)
- DealNotes (NoteId, DealId, NoteText, CreatedAt)
CustomerId, DealId и ProductId — стабильные идентификаторы. DealProducts решает many-to-many: одна сделка может включать много продуктов, и один продукт может появляться во многих сделках. DealNotes хранит заметки отдельно, чтобы не появлялись колонки «Note 1, Note 2, Note 3».
Раньше отчёт вроде «доход по продуктам» требовал разбора строк и надежды, что люди напечатали названия одинаково. После моделирования это простой запрос по DealProducts в соединении с Deals и Products.
Следующие шаги: от схемы к рабочему приложению
Как только схема выглядит правильно на бумаге, перенесите её в реальную базу и протестируйте на реальных данных. Не импортируйте всё сразу. Загрузите небольшой пакет, исправьте проблемы, затем повторяйте.
Практичная последовательность, которая снижает риски:
- Создайте таблицы и связи.
- Импортируйте 50–200 строк, проверьте итоги и выборочные записи.
- Исправьте сопоставления (неправильные колонки, пропущенные ID, дубликаты), затем импортируйте заново.
- Когда всё стабильно — загрузите остальное.
Добавьте правила валидации как можно раньше, чтобы вредные привычки работы с таблицами не вернулись. Сделайте поля обязательными там, где это важно, ограничьте допустимые значения (например, для статуса), валидируйте форматы (даты, email) и используйте внешние ключи, чтобы нельзя было создать заказ для несуществующего клиента.
Затем перестаньте использовать таблицу для правок. Защита данных становится значительно проще, когда люди работают через простые формы и понятные рабочие процессы.
Если вы хотите превратить схему в рабочий внутренний инструмент без написания кода, AppMaster (appmaster.io) может помочь: моделируйте таблицы и связи визуально, затем генерируйте production-ready бэкенд, веб-приложение и нативные мобильные приложения из той же модели.
Вопросы и ответы
Начинайте, когда таблица стала общим источником правды и вы видите дубли, противоречивые значения или отчёты, которые доставляют боль. Если вы постоянно боретесь с «чересчур длинными» колонками, колонками Item 1/Item 2 или постоянными копипастами — реляционная схема быстро сэкономит время.
Если одна строка требует нескольких значений для одного и того же поля — это повторяющаяся группа. Примеры: несколько товаров в одном заказе, несколько адресов у одного клиента, несколько участников у события. Такие данные должны стать дочерней таблицей (или join-таблицей), а не дополнительными колонками или списками в ячейке.
Сделайте копию исходной таблицы только для чтения, затем уберите объединённые ячейки, несколько строк заголовков и итоговые строки из диапазона данных. Приведите колонки к единообразию (один формат даты, одна валюта, один способ обозначать пустые значения), чтобы видеть реальную структуру до проектирования.
По умолчанию используйте авто-сгенерированный ID для каждой таблицы — он стабилен и не меняется, когда люди обновляют email, имя или телефон. Храните реальные идентификаторы (например, email или SKU) как обычные поля и вводите правило уникальности только тогда, когда дубли действительно недопустимы для бизнеса.
Определяйте по владению: если один клиент может иметь много заказов — кладите customer_id в таблицу Orders. Если это many-to-many (заказы и продукты), добавьте join-таблицу, например OrderItems с полями order_id, product_id, количеством и ценой на момент покупки.
Это нужно для предотвращения противоречий. Храните один факт в одном месте. Не обязательно идеальная нормализация — но избавьтесь от дублей вроде одного и того же телефонного номера в пяти строках.
Разбейте на отдельные строки. Ячейка «Email, SMS» сложно фильтруется и валидируется, портит отчёты. Создайте связанную таблицу (или join-таблицу), где каждое значение — отдельная запись, привязанная к родительской строчке.
Разделяйте «текущее состояние» и «историю». Оставьте поле текущего статуса, если оно нужно, но фиксируйте изменения как строки в таблице истории/событий с отметкой времени. Так вы сможете ответить «каким был статус в прошлом месяце?» без догадок.
Сначала импортируйте небольшой набор (примерно 50–200 строк), сверяйте итоги и проверяйте выборочные записи по замороженной копии. Исправьте сопоставления, недостающие ID и дубли, затем повторите импорт. Загружайте всё только тогда, когда процесс стал повторяемым и предсказуемым.
No-code инструменты помогают, если вы хотите, чтобы схема стала рабочим приложением с формами, валидацией и рабочими процессами, а не просто таблицами. С AppMaster (appmaster.io) вы можете визуально моделировать таблицы и связи и сгенерировать production-ready бэкенд, веб-приложение и нативные мобильные приложения из той же модели.


