2015-07-29 2 views
2

У меня есть таблица, содержащая следующее:Postgresql: Преобразование разделенных запятой целые значения и интервалы секвенсированных чисел

Значение является varchar типа для хранения строк символов, как comma и dashes так что все идет. Но обычно он содержит только numbers, comma и dash для указания интервалов.

id | value  | 
------------------ 
1 | 1,2,5,8-10 | 
2 | 1,2,3  | 
3 | 1-3  | 
4 | 1-3, 4-5 | 
5 | 1-2,2-3 | 

Я хочу, чтобы выполнить select запрос, чтобы получить значения в «нормализованной» код-читаемый формат (разделенных запятыми) в уровне базы данных (не в уровне кода), поэтому мне нужно выберите таблицу, чтобы быть чем-то вроде этого.

id | value  | normalized 
------------------------------- 
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, даже если он задает 2 раза, то все равно должны извлекать только 2 раза. Есть ли функция в postgres, которая делает это уже? если нет, то как я разбираю строки и заказываю номера в Postgres sql?

+0

Итак, у вас есть собственный способ хранения набора возможных непересекающихся диапазонов, и вы хотите превратить это в простой набор. –

+0

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

+0

, но вам не следует беспокоиться о том, как вводятся данные, код в формах обрабатывает его. он никогда не будет содержать строки, кроме запятой и тире .. мы позволяем пользователю вводить подобное, потому что это наиболее логичный читаемый способ ввода .. для интервала скажем 330-540 .. вы не можете ожидать от пользователей вход 330,331,332 ... и так далее .. я новичок в postgres и sql вообще .. мало помогает? :) – muffin

ответ

2

Это похоже на хороший пример процедуры в вашем предпочтительном 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, используя слои вложенных подзапросов.

+0

+ upvote для действительно полезного объяснения и рекомендаций по чтению .. это действительно будет полезно для начинающих, таких как я :) – muffin

Смежные вопросы