F.48. seg

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

F.48.1. Обоснование

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

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

Более того, вследствие нечёткости границ использование традиционных числовых типов данных приводит к определённой потере информации. Рассмотрим такой пример: ваш инструмент выдаёт 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)

F.48.2. Синтаксис

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

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

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

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

Таблица F.29. Примеры допустимых вводимых значений 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...2323 или 0.23. Для разрешения этой неоднозначности во входных числах seg перед десятичной точкой всегда должна быть минимум одна цифра.

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

F.48.3. Точность

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

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

F.48.4. Использование

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

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

ОператорОписание
[a, b] << [c, d][a, b] полностью находится левее [c, d]. То есть, [a, b] << [c, d] — true, если b < c, и false в противном случае.
[a, b] >> [c, d][a, b] полностью находится правее [c, d]. То есть, [a, b] >> [c, d] — true, если a > d, и false в противном случае.
[a, b] &< [c, d]Пересекает или левее — Ещё лучше это читается как «не простирается правее». Результатом будет true, когда b <= d.
[a, b] &> [c, d]Пересекает или правее — Ещё лучше это читается как «не простирается левее». Результатом будет true, когда a >= c.
[a, b] = [c, d]Равенство — сегменты [a, b] и [c, d] равны, то есть, a = c и b = d.
[a, b] && [c, d]Сегменты [a, b] и [c, d] пересекаются.
[a, b] @> [c, d]Сегмент [a, b] содержит сегмент [c, d], то есть, a <= c и b >= d.
[a, b] <@ [c, d]Сегмент [a, b] содержится в [c, d], то есть, a >= c и b <= d.

(До версии PostgreSQL 8.2 операторы включения @> и <@ обозначались соответственно как @ и ~. Эти имена по-прежнему действуют, но считаются устаревшими и в конце концов будут упразднены. Заметьте, что старые имена произошли из соглашения, которому раньше следовали ключевые геометрические типы данных!)

Также поддерживаются стандартные операторы для B-дерева, например:

ОператорОписание
[a, b] < [c, d]Меньше
[a, b] > [c, d]Больше

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

F.48.5. Замечания

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

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

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

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

F.48.6. Благодарности

Первый автор: Джин Селков мл. , Аргоннская национальная лаборатория, Отдел математики и компьютерных наук

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