Это похоже на хороший пример процедуры в вашем предпочтительном PL или простое расширение C. pl/perl, pl/pythonu или pl/v8 были бы моими выборами.
Сказанное, это достаточно просто в SQL. Разделите, чтобы найти поддиапазоны, которые могут быть одной цифрой или диапазоном. Затем для каждого диапазона generate_series над ним.
.: например
SELECT n
FROM
regexp_split_to_table('1,2,5,8-10', ',') subrange,
regexp_split_to_array(subrange, '-') subrange_parts,
generate_series(subrange_parts[1]::integer,
coalesce(subrange_parts[2], subrange_parts[1])::integer
) n;
который можно обернуть как SQL
функции, или использовать как часть запроса над столом.
Применительно к таблице, вы получите что-то вроде:
CREATE TABLE example
("id" int, "value" varchar)
;
INSERT INTO example
("id", "value")
VALUES
(1, '1,2,5,8-10'),
(2, '1,2,3'),
(3, '1-3'),
(4, '1-3, 4-5'),
(5, '1-2,2-3')
;
При применении к столу, что-то вдоль линий:
SELECT
example.id,
array_agg(DISTINCT n) AS expanded_set
FROM
example,
regexp_split_to_table(example.value, ',') subrange,
regexp_split_to_array(subrange, '-') subrange_parts,
generate_series(subrange_parts[1]::integer,
coalesce(subrange_parts[2], subrange_parts[1])::integer
) n
GROUP BY
example.id;
Результата (с оригинальной седловиной добавленной):
id | original_format | expanded_set
----+-----------------+----------------
1 | 1,2,5,8-10 | {1,2,5,8,9,10}
2 | 1,2,3 | {1,2,3}
3 | 1-3 | {1,2,3}
4 | 1-3, 4-5 | {1,2,3,4,5}
5 | 1-2,2-3 | {1,2,3}
(5 rows)
Это не будет особенно быстро, но все может быть в порядке. Если нет, напишите что-нибудь быстрее в C как расширение, или, может быть, plperl или что-то еще.
Чтобы понять, что происходит, читайте разделы руководства PostgreSQL на:
GROUP BY
и агрегациях
- Агрегатных функций, в частности,
array_agg
DISTINCT
как агрегация классификатор
- PostgreSQL массивов, которые я использовать здесь как промежуточное состояние и результат
- Функция
generate_series
- В
regexp_split_to_table
и regexp_split_to_array
функции
LATERAL
запросы, которые используются неявно здесь потому, что одна функция потребляет результаты из другой функции в списке присоединиться.
Приведенный выше пример будет работать только в PostgreSQL 9.2 и новее. Если у вас установлена более старая версия, вам придется работать с отсутствием LATERAL
, используя слои вложенных подзапросов.
Итак, у вас есть собственный способ хранения набора возможных непересекающихся диапазонов, и вы хотите превратить это в простой набор. –
да точно ... потому что данные принимаются как простая строка, подобная той, что и пользователи, которые ее вводят, и хотят получить ее таким образом. – muffin
, но вам не следует беспокоиться о том, как вводятся данные, код в формах обрабатывает его. он никогда не будет содержать строки, кроме запятой и тире .. мы позволяем пользователю вводить подобное, потому что это наиболее логичный читаемый способ ввода .. для интервала скажем 330-540 .. вы не можете ожидать от пользователей вход 330,331,332 ... и так далее .. я новичок в postgres и sql вообще .. мало помогает? :) – muffin