Типы чисел с фиксированной точкой в ​​Postgres?

База данных Postgres, которой я управляю, содержит множество столбцов, содержащих числа от 0 до 100, с фиксированным (никогда не более 3, обычно 2) количеством знаков после запятой. Большинство этих чисел группируются вокруг своих соответствующих средних и, таким образом, часто повторяются, что при правильной реализации может сделать их идеальными типами для индексации GIN и GIST. Это очень хорошо согласуется с нашими планами на будущее, поскольку GIN и/или GIST понадобятся для индексации определенных типов запросов, которые мы планируем реализовать в будущем. Функциональность, поставляемая с диапазонами, также будет полезна, что также станет доступны, если они были сохранены как целые числа.

В настоящее время они хранятся как числа с плавающей запятой, но в прошлом это вызывало проблемы при поиске чисел по их точному значению. Я знаю о классе NUMERIC в Postgres, но, похоже, он делает противоположное тому, что я хочу, в отношении производительности. Мы почти никогда не будем выполнять какие-либо математические операции с этими числами в Postgres, и для любой математики, которую мы делаем, она будет чрезвычайно простой, и точность не будет иметь значения. Таким образом, эти столбцы кажутся идеальными кандидатами для типов чисел с фиксированной точкой. По сути, я хочу, чтобы они хранились внутри как целые числа, но при возврате в запросах и при установке через UPDATE и INSERT десятичная точка должна перемещаться, скажем, на три знака влево.

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

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


person virnovus    schedule 02.03.2016    source источник
comment
Сохраните их как целые числа * 1000 (например, 42,42 будет храниться как 42420), и когда вы выберете значения, снова разделите их.   -  person a_horse_with_no_name    schedule 03.03.2016
comment
Я думал об этом, но весь наш код, работающий поверх базы данных, ожидает получить число в определенном формате. Исправить весь этот код было бы огромным беспорядком, да и реализация не очень элегантная. Отсюда и возможность делать это с помощью триггеров. SELECT, INSERT и UPDATE получат триггеры для деления или умножения на 1000 в зависимости от того, в каком направлении идут данные. Я понятия не имею, как это повлияет на производительность.   -  person virnovus    schedule 03.03.2016
comment
Почему бы тогда не использовать представления?   -  person a_horse_with_no_name    schedule 03.03.2016
comment
Обновляемые представления кажутся подходящим вариантом, если производительность приемлема. Написание пользовательского типа на C потребует более высоких затрат на настройку и обслуживание (например, для разработки). Вы должны протестировать оба.   -  person hruske    schedule 03.03.2016
comment
Мы могли использовать представления. Я рассматривал возможность использования материализованного представления для текущих данных, а затем архивирования исторических данных на более медленные носители. Но размер базы данных составляет всего около 15 ГБ, а наш серверный комплект поставляется с твердотельным накопителем на 160 ГБ, поэтому хранение не является приоритетом. Однако создание собственного типа позволило бы нам использовать эти типы и в других таблицах, а не только в нашей основной. Использование представлений потребовало бы новых представлений для каждой таблицы, с которой мы хотели это сделать.   -  person virnovus    schedule 03.03.2016


Ответы (1)


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

http://pgxn.org/tag/type/

Там есть две десятичные реализации с фиксированной запятой, основанные на 32/64-битном хранилище. Первый из них написан Павлом Штехуле, известным членом сообщества PostgreSQL. Я не использовал ни то, ни другое и не смотрел код, но он должен стать хорошей отправной точкой для тестов.

person Richard Huxton    schedule 03.03.2016
comment
Стоит отметить, что в Postgres уже есть числовой тип с фиксированной точкой, money, поэтому, если вы решили создать свой собственный, вы, вероятно, могли бы украсть большую часть код. - person Nick Barnes; 03.03.2016
comment
Я много писал на C во время своей магистерской программы, так что со мной все будет в порядке. Что касается переносимости, как насчет отправки кода в качестве расширения? Если бы я пошел по этому пути, какое имя типа было бы предпочтительным? фиксированный4p2? исправлено4_2? исправлено42? фиксированный4(2)? Не уверен, что соглашение будет в этом случае. Потребовалось бы много дополнительной работы, чтобы создать тип, который принимал бы параметр для количества цифр, например числовой? Это, вероятно, будет связано с нашими долгосрочными планами по настройке индексов GIN и GIST, поэтому нам придется довольно далеко зайти в сорняки с нашим кодом C в том виде, в каком он есть. Спасибо за помощь! - person virnovus; 03.03.2016
comment
@NickBarnes Хм, похоже, тип денег основан на bigint, а не на числовом значении, как я думал. Хороший звонок. - person virnovus; 03.03.2016
comment
@virnovus: проблема с параметрами типа (типмодами) заключается в том, что значения обычно не поставляются с прикрепленным типоммодом. Вы (потенциально) получите его в подпрограмме ввода-вывода или приведения, но в любой из ваших арифметических/сравнительных функций вы сами по себе. Другими словами, вам нужно будет втиснуть типмод в первые несколько бит базового целого числа и учитывать его во всех ваших вычислениях. Так что это немного усложняет ситуацию, но если вы хотите выпустить его как реализацию с фиксированной точкой общего назначения, то я думаю, что это был бы способ сделать это. - person Nick Barnes; 04.03.2016