простой вопрос оформления стола - PullRequest
7 голосов
/ 02 февраля 2010

Я пытаюсь немного подумать и избежать какой-то дополнительной боли, если это возможно.

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

Если у вас есть базовая таблица, такая как ниже, целесообразно ли и / или более эффективно включать поле, которое включает вычисление из информации, которую можно найти из двух других столбцов. IE:

+-----+---------+------------+-------+--------+-------+
| id  | room_id | bookdate   | price | people | total |
+-----+---------+------------+-------+--------+-------+
| 414 | 132     | 2010-03-01 | 14.55 | 2      | 29.10 |
| 415 | 132     | 2010-03-02 | 14.55 | 2      | 29.10 |
| 416 | 132     | 2010-03-03 | 14.55 | 2      | 29.10 |
+-----+---------+------------+-------+--------+-------+

Информация в последнем поле может быть извлечена из произведения двух предыдущих, поэтому она является избыточной и ненужной. Есть ли случаи, когда это все еще стоит иметь?

Ответы [ 11 ]

6 голосов
/ 02 февраля 2010

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

Это классический компромисс между производительностью и хранением.Я бы порекомендовал вычислять значение до тех пор, пока вам не понадобится повышение производительности.

4 голосов
/ 02 февраля 2010

Может быть, создать таблицу, которая содержит все поля, кроме последнего, а затем создать представление, которое содержит все поля и автоматически подсчитывает последние поля?

Таким образом, таблица будет содержать только эти поля

+-----+---------+------------+-------+--------+
| id  | room_id | bookdate   | price | people | 
+-----+---------+------------+-------+--------+
| 414 | 132     | 2010-03-01 | 14.55 | 2      | 

И определение представления, которое вычисляет итог, также очень просто:

select *, price*people as total  from rooms

(при условии, что ваша таблица называется rooms

2 голосов
/ 03 февраля 2010

Я часто за расчетное поле, если вы делаете это правильно, определяя поле в базе данных как рассчитанное. Таким образом, расчет всегда применим независимо от того, как изменяются данные. Я бы сделал это только в том случае, если вам нужно получать эти расчеты в отчетах, которые содержат много записей. Конечно, легко написать формулу в запросе, но если вы вычисляете это число часто, вы тратите ресурсы сервера (вычисляемое поле вычисляет только при изменении информации) и, возможно, серьезно замедляете запрос, если он должен выполнять вычисления для миллионов записей для отчетов. Материализованное представление также является хорошей идеей (поскольку оно будет предварительно рассчитывать), но обычное представление просто несколько раз выводит вас из калькуляции, поскольку оно не дает преимущества в производительности вычисляемого поля. С другой стороны, я никогда не создаю представления, если мне это не нужно (т.е. я могу решить проблему другим способом), поскольку они могут привести к реальным проблемам с производительностью, когда люди начинают создавать представления поверх представлений. Не используйте молоток, когда вам нужна отвертка.

Вычисляемые поля являются мощными инструментами при правильном использовании и часто игнорируются разработчиками баз данных.

2 голосов
/ 03 февраля 2010

Если вы решите денормализовать производительность чтения, вы можете добавить проверочное ограничение для обеспечения согласованности.

create table rooms (
    price numeric, 
    people numeric, 
    total numeric check (total=price*people));

Это добавит небольшие накладные расходы на вставки и обновления.

2 голосов
/ 02 февраля 2010

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

1 голос
/ 03 февраля 2010

Если вас беспокоит выбор производительности (по крайней мере, с общей суммой WHERE = xx.xx), вы можете просто добавить индекс.

CREATE INDEX booking_total при бронировании ((цена * чел.));

Это изменит план запроса для SELECT * from booking where price*people = 58.2; из этого;

Seq Scan on booking (cost=0.00..299.96 rows=60 width=24) (actual time=0.015..2.926 rows=1 loops=1) Filter: ((price * (people)::double precision) = 58.2::double precision) Total runtime: 2.947 ms

к этому

Bitmap Heap Scan on booking (cost=4.30..20.83 rows=5 width=24) (actual time=0.016..0.016 rows=1 loops=1) Recheck Cond: ((price * (people)::double precision) = 58.2::double precision) -> Bitmap Index Scan on booking_total (cost=0.00..4.29 rows=5 width=0) (actual time=0.009..0.009 rows=1 loops=1) Index Cond: ((price * (people)::double precision) = 58.2::double precision) Total runtime: 0.044 ms

PostgreSQL пород: -)

1 голос
/ 02 февраля 2010

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

В противном случае это вопрос нормализации . Иногда допустима денормализация таблицы. Денормализация , особенно в такой среде, как хранилище данных, может использоваться для повышения производительности. Однако важно убедиться, что ваши данные остаются согласованными. Другими словами, вам нужно убедиться, что ваше поле total обновляется при изменении price или people.

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

Примечание: таблица не может быть 3NF (третья нормальная форма), пока вычисленные поля не будут удалены.

0 голосов
/ 02 февраля 2010

Поскольку вы можете вычислить значение - довольно легко в этом случае - оно является избыточным. Вы почти никогда не должны хранить избыточные данные. Это означает, что в каждом месте, где вы обновляете либо цену, либо людей, вы должны обязательно обновлять общее. Если вы забудете сделать это хотя бы в одном месте, данные будут противоречивыми. Итак, предположим, что теперь у вас есть запись, в которой говорится, что цена = 10 долларов, люди = 3, всего = 40 долларов. Если у вас есть разные программы, отображающие информацию по-разному - разные итоги или подмножества или что-то еще - пользователь может получить разные ответы на один и тот же вопрос в зависимости от того, как он его задал. Хотя неправильно получить неправильный ответ, еще хуже иногда получить правильный ответ, а иногда и неправильный ответ, потому что тогда может быть неясно, как решить проблему. Я имею в виду, что если я вижу, что определенный клиент показывает 2 человека, когда он должен показать 3, возможно, есть какой-то экран, на который я могу перейти, переписать 2 с помощью 3, нажать сохранить или что-то еще, и это исправлено. Но если там написано: 10 раз, 2 человека = 30 долларов, куда мне его починить? Как?

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

Я сейчас работаю над системой, которая заполнена избыточными данными. Основная информация о каждом из продуктов нашей компании хранится в таблице «пункт». Для каждой единицы в наличии у нас есть запись запаса, и вместо того, чтобы просто ссылаться на запись позиции, они копируют все данные для каждой единицы запаса. Когда товар продается, мы копируем все данные в запись о продаже. Если что-то возвращается, мы копируем все данные в возвращаемую запись. И т. Д. Для некоторых других типов записей. Это вызывает бесконечные неприятности. Однажды у нас возникла проблема, когда пользователь выполнил запрос в поисках элементов с определенными характеристиками, а в список попаданий были включены элементы, которые не соответствовали критериям поиска. Зачем? Поскольку запрос находит все записи изделия, которые соответствуют критериям поиска, он пытается сопоставить эти записи изделия с записями запаса по номеру детали ... но некоторые записи запаса не соответствуют записи изделия по другим критериям по различным причинам. Сейчас я работаю над устранением проблемы, при которой данные о расходах не всегда должным образом копируются из записей о запасах в записи о продажах. Я хотел бы просто перепроектировать базу данных, чтобы исключить все лишние данные, но это был бы огромный проект.

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

Умножать два числа вместе в записи, которую вы уже читаете? Ни за что. Я не могу представить, что это может вызвать проблемы с производительностью. Если ваш движок базы данных не может умножить два числа на крошечный процент от времени, необходимого для чтения записи, получите новый движок базы данных.

0 голосов
/ 02 февраля 2010

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

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

0 голосов
/ 02 февраля 2010

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

ВЫБРАТЬ очень легко, используя это поле: ВЫБЕРИТЕ цену, чел., (Цена * чел.) КАК всего ОТ some_table;

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

BR

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...