Дублировать столбец для более быстрых запросов?

30

Название не имеет особого смысла, но я не мог придумать лучшего названия для этой проблемы.

У меня есть следующие таблицы

проектов

  • мне бы
  • название

Клиенты

  • мне бы
  • id_project
  • название

платежи

  • мне бы
  • id_customer
  • свидание
  • сумма

Когда пользователь входит в систему, он получает доступ к определенному проекту. Теперь я хочу перечислить все платежи за этот проект, и это должно быть довольно просто:

SELECT FROM payments where id_customer in (SELECT id from customers where id_project = 5)

Мой вопрос: если не лучше добавить столбец id_project в таблицу платежей, то запросы будут проще и быстрее.

Габриэль Соломон
источник
1
таким образом, запрос не является проблемой для современных РСУБД (или, лучше, используйте соединение).
Гарик
4
Согласитесь, получите план запроса для подвыбора против объединения и посмотрите, какой из них лучше
Гай
1
Я думаю , что это ТАК пост стоит посмотреть, так как @igor упоминалось об использовании JOIN или IN
CoderHawk

Ответы:

52

Кажется, вы спрашиваете, имеет ли смысл денормализация .

Денормализация - это процесс попыток оптимизировать производительность чтения базы данных путем добавления избыточных данных или группировки данных. В некоторых случаях денормализация помогает скрыть недостатки, присущие программному обеспечению реляционных баз данных. Нормализованная реляционная база данных создает большую нагрузку доступа к физическому хранилищу данных, даже если она хорошо настроена на высокую производительность.

Ответ всегда "это зависит", поэтому вот мое эмпирическое правило:

Если ...

  • объем данных не велик
  • Вы не делаете тонны соединений уже
  • и / или производительность базы данных в настоящее время не является узким местом

тогда оставайся нормализованным . Да, денормализация происходит быстрее, но это также означает, что у вас есть избыточные данные в системе - данные, которые необходимо поддерживать и синхронизировать. Больше нет «одного источника» для этих данных, но есть несколько источников, которые могут отклоняться. Это рискованно с течением времени, поэтому вы не должны делать это, если у вас нет веских причин для этого, подкрепленных некоторыми тестами.

Я бы только денормализовал, когда ...

  • объем данных очень большой
  • Объединения стоят дорого, и вы должны сделать много из них, чтобы получить даже тривиальные запросы
  • производительность базы данных является узким местом и / или вы хотите идти как можно быстрее

Операции на современном оборудовании выполняются очень быстро, но они никогда не бывают бесплатными.

Джефф Этвуд
источник
9

Вам было бы лучше переписать запрос как:

SELECT payments.*
FROM   customers
JOIN   payments 
ON     payments.id_customer = customers.id
WHERE  customers.id_project = 5

Хотя это кажется менее лаконичным, и хороший планировщик запросов увидит, что вы пытаетесь сделать, и вместо этого выполнит коррелированный подзапрос в качестве вышеуказанного соединения, плохой планировщик запросов может в конечном итоге выполнить сканирование индекса payments.id_customer(при условии, что у вас есть соответствующий индекс ) (или, что еще хуже, сканирование таблицы) вместо того, чтобы делать вещи более эффективным способом. Даже хороший планировщик запросов может не увидеть оптимизацию, если расположение этого запроса заключено в нечто более сложное. Выражение отношений в виде объединения, а не подзапроса может иметь большее значение, чем изменение структуры данных.

Как говорит Джефф, любая денормализация должна рассматриваться с осторожностью - она ​​может легко повысить производительность, особенно для некоторых целей отчетности, но может привести к несогласованности из-за ошибок в поддерживающей бизнес-логике.

В качестве примечания: Очевидно, я не знаю вашего бизнеса, поэтому я мог что-то упустить, но ваши отношения за столом кажутся мне странными. Они подразумевают, что у вас никогда не может быть более одного проекта с одним и тем же клиентом, что обычно не соответствует моему опыту, по крайней мере, в течение длительного периода.

customer     project      payment
--------     --------     -------
                          pa_id
             pr_id    <-- payment
cu_id    <-- customer     

или если быть менее нормализованным (хотя я сомневаюсь, что это будет необходимо):

customer     project      payment
--------     --------     --------
                          pa_id
             pr_id    <-- payment
cu_id    <-- customer 
           `------------- customer    

Конечно, это все еще исключает возможность совместного проекта с двумя клиентами ...

Дэвид Спиллетт
источник
3
Первое правило производительности: никогда не используйте * в производстве!
Брайан Баллсун-Стэнтон
@ Брайан: очень верный момент. А также потенциальное влияние на производительность, исключающее * в предложениях select, также позволяет избежать проблем с упорядочением столбцов в представлениях на представлении в MSSQL, если sys.depends выходит из строя из-за использования DROP VIEW+ CREATE VIEWвместо ALTER VIEW.
Дэвид Спиллетт
@ Брайан, я положил * для простоты написания.
Габриэль Соломон
Проект представляет собой скорее самостоятельное приложение со своим доменом и принадлежит разным клиентам, поэтому заказчик не может иметь одну и ту же учетную запись в разных проектах
Габриэль Соломон
4

В некоторых базах данных у вас есть возможность создавать «материализованные представления» вместо сложных VIEWS с большим объемом данных на основе сложного запроса. Это можно использовать, чтобы избежать денормализации в исторически сложившейся системе приложений. Если вы решите использовать » Материализованные представления "у вас должно быть четкое представление о методах обновления и объеме хранилища, которое будет использоваться материализованным представлением ...

Кристоф Преттнер
источник