Извлечение чисел в последовательном порядке из случайно упорядоченного столбца

#postgresql

#postgresql

Вопрос:

У меня есть один столбец в моей базе данных, qs5 который содержит ряд вариантов множественного выбора 1-4, которые могут отображаться в любом случайном порядке, разделенных пробелом, например 1 2 4 или 4 1

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

 |qs5c1|qs5c2|qs5c3|qs5c4|
|-----|-----|-----|-----|
|  1  |  2  |null |  4  |
|  1  |null |null |  4  |
  

Это SQL, который я использовал

 substring('1' from position('1' in qs5) for 1) as qs5c1,
substring('2' from position('2' in qs5) for 1) as qs5c2,
substring('3' from position('3' in qs5) for 1) AS qs5c3,
substring('4' from position('4' in qs5) for 1) AS qs5c4
  

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

 |qs5c1|qs5c2|qs5c3|qs5c4|
|-----|-----|-----|-----|
|  1  |null |null |null |
|null |null |null |  4  |
  

Я думаю, что когда он извлекает первый символ, длина qs5 на 1 символ меньше, чем было ранее, но опять же, это не объясняет, почему вторая строка пропускает 1, поскольку 1 должен быть в том же месте до / после извлечения 4.

Спасибо!

Ответ №1:

Проблема в том, что вы не substring() из qs5 , а из литералов '1' , '2' и т.д. поэтому, конечно, это может привести только к этому единственному символу в литерале. Если вы используете qs5 вместо этого, это работает так, как вы задумали.

 SELECT substring(qs5 FROM position('1' IN qs5) FOR 1) qs5c1,
       substring(qs5 FROM position('2' IN qs5) FOR 1) qs5c2,
       substring(qs5 FROM position('3' IN qs5) FOR 1) qs5c3,
       substring(qs5 FROM position('4' IN qs5) FOR 1) qs5c4
       FROM elbat;
  

db<>скрипка

Ответ №2:

Если это действительно фиксированный набор значений, я бы сделал это так:

 select case when '1' = any(string_to_array(qs5, ' ')) then '1' end as qs51,
       case when '2' = any(string_to_array(qs5, ' ')) then '2' end as qs52,
       case when '3' = any(string_to_array(qs5, ' ')) then '3' end as qs53,
       case when '4' = any(string_to_array(qs5, ' ')) then '4' end as qs54,
       case when '5' = any(string_to_array(qs5, ' ')) then '5' end as qs55
from the_table;
  

Онлайн-пример:https://rextester.com/FMR86051

Если вам нужно это более динамичным способом, вы можете использовать что-то вроде этого:

 select f.options[1] as qs5c1,
       f.options[2] as qs5c2,
       f.options[3] as qs5c3,
       f.options[4] as qs5c4
from the_table t
  cross join lateral (
    select array_agg(x.val order by g.idx) as options
    from generate_series(1,4) g(idx)
      left join unnest(string_to_array(t.qs5, ' ')::int[]) with ordinality as x(val,idx) on g.idx = x.val
  ) f
;
  

Онлайн-пример с 8 элементами:https://rextester.com/PMKDW38472

Комментарии:

1. итак, на самом деле это 8, я просто сократил до 4 ради этого примера. как вы думаете, с 8 все будет в порядке?

2. @ClayCrosby: да, вы можете сделать то же самое для 8 значений. Я добавил более общее (но более сложное) решение для сопоставления элементов с правильным индексом столбца.