seg

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

Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.


Обоснование

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

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

Если и далее рассуждать здраво, размытость границ предполагает, что использование традиционных числовых типов данных приводит к определенной потере информации. Рассмотрим такой пример: ваш инструмент выдает 6.50, и вы вводите это значение в базу данных. Что вы получите, выбрав его из базы? Смотрите:

test=> SELECT 6.50 :: float8 AS "pH";
 pH
---
6.5
(1 row)

В мире измерений 6.50 — это не то же самое, что 6.5. Разница между этими измерениями иногда может быть критической. Экспериментаторы обычно записывают (и публикуют) цифры, в которых уверены. Запись 6.50 на самом деле представляет собой нечеткий интервал, содержащийся внутри большего и еще более нечеткого интервала 6.5, и единственное, что у них может быть общего, — это их центральные точки. Мы определенно не хотим, чтобы такие разные элементы данных выглядели одинаково.

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

Проверьте это:

test=> SELECT '6.25 .. 6.50'::seg AS "pH";
          pH
------------
6.25 .. 6.50
(1 row)

Синтаксис

Внешнее представление интервала образуется одним или двумя числами с плавающей запятой, соединенными оператором диапазона (.. или ...). Как вариант, интервал можно задать центральной точкой плюс/минус отклонение. Также этот тип позволяет сохранить дополнительные индикаторы достоверности (<, > или ~). (Однако индикаторы достоверности игнорируются всеми встроенными операторами.) В Таблице 27 дается обзор допустимых представлений; в Таблице 28 приводятся некоторые примеры.

В Таблице 27 символы x, y и delta обозначают числа с плавающей запятой. Перед значениями x и y, но не delta, может быть добавлен индикатор достоверности.

Таблица 27. Внешние представления seg

ПредставлениеОписание
xОдно значение (интервал нулевой длины)
x .. yИнтервал от x до y
x (+-) deltaИнтервал от x - delta до x + delta
x ..Открытый интервал с нижней границей x
.. xОткрытый интервал с верхней границей x

Таблица 28. Примеры допустимых вводимых значений seg

ПримерЧто делает
5.0Создает сегмент нулевой длины (или точку, если хотите).
~5.0Создает сегмент нулевой длины и записывает ~ в данные. Знак ~ игнорируется при операциях с seg, но сохраняется как комментарий.
<5.0Создает точку с координатой 5.0. Знак < игнорируется, но сохраняется как комментарий.
>5.0Создает точку с координатой 5.0. Знак > игнорируется, но сохраняется как комментарий.
5(+-)0.3Создает интервал 4.7 .. 5.3. Обратите внимание, что запись (+-) не сохраняется.
50 ..Все, что больше или равно 50.
.. 0Все, что меньше или равно 0.
1.5e-2 .. 2E-2Создает интервал 0.015 .. 0.02.
1 ... 2То же, что и 1...2, или 1 .. 2, или 1..2 (пробелы вокруг оператора диапазона игнорируются).

Поскольку оператор ... широко используется в источниках данных, он принимается в качестве альтернативного написания оператора ... К сожалению, это порождает неоднозначность при разборе: неясно, какая верхняя граница подразумевается в записи 0...23 — 23 или 0.23. Для разрешения этой неоднозначности во входных числах seg перед десятичной точкой всегда должна быть как минимум одна цифра.

В целях контроля корректности seg не принимает интервалы с нижней границей, превышающей верхнюю, например 5 .. 2.


Точность

Значения seg хранятся внутри как пары 32-битных чисел с плавающей запятой. Это значит, что числа с более чем 7 значащими цифрами будут усекаться.

Числа, содержащие 7 значащих цифр и меньше, сохраняют изначальную точность. То есть, если запрос возвращает 0.00, вы можете быть уверены, что конечные нули не являются артефактами форматирования: они отражают точность исходных данных. Количество ведущих нулей не влияет на точность: значение 0.0067 считается имеющим только 2 значащие цифры.


Применение

Модуль seg включает класс операторов индекса GiST для значений seg. Операторы, поддерживаемые этим классом операторов, перечислены в Таблице 29.

Таблица 29. Операторы seg для GiST

Оператор
Описание
seg << seg → boolean
Первый seg полностью находится левее второго? [a, b] << [c, d] — true, если b < c.
seg >> seg → boolean
Первый seg полностью находится правее второго? [a, b] >> [c, d] — true, если a > d.
seg &< seg → boolean
Первый seg не простирается правее второго? [a, b] &< [c, d] — true, если b <= d.
seg &> seg → boolean
Первый seg не простирается левее второго? [a, b] &> [c, d] — true, если a >= c.
seg = seg → boolean
Два интервала seg равны?
seg && seg → boolean
Два интервала seg пересекаются?
seg @> seg → boolean
Первый интервал seg содержит второй?
seg <@ seg → boolean
Первый интервал seg содержится во втором?

Помимо вышеописанных операторов для типа seg доступны обычные операторы сравнения, показанные в таблице Операторы сравнения. Эти операторы сначала сравнивают (a) с (c), и если они равны, сравнивают (b) с (d). В большинстве случаев это приводит к адекватному упорядочиванию, что полезно, если нужно применить ORDER BY с этим типом.


Примечания

Примеры использования можно увидеть в регрессионном тесте sql/seg.sql.

Механизм, преобразующий (+-) в обычные диапазоны, не вполне точно определяет число значащих цифр для границ. К примеру, он добавляет дополнительную цифру к нижней границе, если результирующий интервал включает степень десяти:

qhb=> SELECT '10(+-)1'::seg AS seg;
      seg
---------
9.0 .. 11             -- должно быть: 9 .. 11

Производительность индекса R-дерева может значительно зависеть от начального порядка вводимых значений. Может быть очень полезно отсортировать входную таблицу по столбцу seg; пример можно найти в скрипте sort-segments.pl.


Благодарности

Оригинальный автор: Джин Селков мл. (Gene Selkov, Jr.), selkovjr@mcs.anl.gov, Аргоннская национальная лаборатория, Отдел математики и компьютерных наук.

Я благодарен в первую очередь профессору Джо Геллерштейну (Joe Hellerstein) (https://dsf.berkeley.edu/jmh/) за пояснение сути GiST (http://gist.cs.berkeley.edu/). Я также признателен всем разработчикам Postgres в настоящем и прошлом за возможность создать мой собственный мир и спокойно жить в нем. Еще я хотел бы выразить признательность Аргоннской лаборатории и Министерству энергетики США за годы постоянной поддержки моих исследований в области баз данных.