Pivot в Postgresql с отметками TRUE / FALSE - PullRequest
0 голосов
/ 20 февраля 2019

Интересно, как поместить несколько значений массива в имена столбцов со значениями TRUE / FALSE.Я приведу вам конкретный пример:

У меня есть повторяющиеся строки с дублированием последнего столбца из-за разных результатов:

DATE        ID    Species   Illness         Tag
20180101    001   Dog       Asthma          Mucus
20180101    001   Dog       Asthma          Noisy
20180101    001   Dog       Asthma          Respiratory
20180102    002   Cat       Osteoarthritis  Locomotor
20180102    002   Cat       Osteoarthritis  Limp
...
20180131    003   Bird      Avian Pox       Itchy

Я хочу получить следующее:

DATE        ID    Species   Illness      Mucus  Noisy ... Limp  Itchy 
20180101    001   Dog       Asthma       TRUE   TRUE  ... FALSE FALSE
20180102    002   Cat       Osteoarth.   FALSE  FALSE ... TRUE  FALSE
...
20180131    003   Bird      Avian Pox    FALSE  FALSE ... FALSE TRUE

Я пытался использовать функцию "кросс-таблица" только для части тегов, но она выдает ошибку несуществующей функции:

 select * 
 from crosstab (
   'select c.id, tg."name"  
    FROM taggings t 
    join consultations c
      on c.id=t.taggable_id
    join tags tg 
      on t.tag_id=tg.id
    group by c.id, tg."name"'
 ) as final_result(dermatological BOOLEAN, behaviour BOOLEAN)

Кстати.У меня около 350 тегов, так что это не самая оптимальная функция: /

РЕДАКТИРОВАТЬ: Наконец, я добавил расширение tablefunc, и я попытался с crosstab (), но я получил следующую ошибку:

Сбой выполнения запроса Причина: ошибка SQL [22023]: ОШИБКА: недопустимый оператор SQL данных источника. Подробно: предоставленный SQL должен возвращать 3 столбца: rowid, category и values.

попробуйте найти решение и обновить его здесь, но если кто-то знает, как его решить, пожалуйста, поделитесь :) Спасибо!


После нескольких дней чтения и пробования предлагаемых решений, это сработало для меня:

Я получил 3 отдельные таблицы, а затем соединил первую и третью, чтобы получить необходимую мне информацию, плюс теги в виде столбцов со значением 1/0, если тег существует в определенномЯ БЫ.Еще одно правление => Мне на самом деле не нужна дата, поэтому я основал таблицы на идентификаторах консультаций.

ТАБЛИЦА 1: Получите таблицу всех столбцов, сгруппированных поИдентификаторы и получите все теги, которые есть у одного идентификатора.

ID    Species   Age      Illness         Tag
001   Dog        2       Asthma          Mucus
001   Dog        2       Asthma          Noisy
001   Dog        2       Asthma          Respiratory
002   Cat        5       Osteoarthritis  Locomotor
002   Cat        5       Osteoarthritis  Limp
...
003   Bird       1       Avian Pox       Itchy

ТАБЛИЦА 2: Получите декартово произведение, которое будет пересекать все консультации со списком всех различных тегов, и закажите ихдля функции crosstab ().(функция кросс-таблицы должна иметь 3 столбца; ID, теги и значения)

With consultation_tags as
    (here put the query of the TABLE 1),
tag_list as
    (select tags."name"
    from tags
    join taggings t on t.tag_id = tags.id
    join consultations c on c.id = t.taggable_id a
    group by 1), —-> gets the list of all possible tags in the DB 
cartesian_consultations_tags as
    (select consultations_tags.id, tag_list.name,
     case when tag_list.name = consultations_tags.tag_name then 1
     else 0  --> "case" gets the value 1/0 if the tag is present in an ID
     end as tag_exists
    from
    consultations_tags
    cross join 
    tag_list)
select cartesian_consul_tags.id, cartesian_consul_tags.name, 
SUM(cartesian_consul_tags.tag_exists) --> for me, the values were duplicated, and so were tags
from cartesian_consul_tags
group by 1, 2
order by 1, 2

-> порядок тегов здесь действительно важен, потому что именно вы называете столбцы в функции кросс-таблицы;он не преобразует определенный тег в столбец, он только передает значение этой позиции тега, поэтому, если вы запутаете порядок именования, значения не будут соответствовать правильно.

ТАБЛИЦА 3: Кросс-таблица второй таблицы -> она поворачивает декартову таблицу произведений, или в этом случае ТАБЛИЦУ 2.

SELECT * 
FROM crosstab(‘ COPY THE TABLE 2 ‘) --> if you have some conditions like “where species = ‘Dogs’”, you will need to put double apostrophe in the string value —> where species = ‘’Dogs’’
AS ct(id int4,”Itchy” int8,
“Limp” int8,
“Locomotor” int8,
“Mucus” int8,
“Noisy” int8) --> your tag list. You can prepare it in excel, so all the tags are in quotation marks and has corresponding datatype. The datatype of the tags has to be the same as the datatype of the “value” in the table 2 

НАКОНЕЦ , финальная таблица, которую я хотел, была присоединиться к таблицам1 и 3, поэтому мне нужна информация из идентификаторов консультаций и список тегов в виде столбцов со значениями 0/1, если тег присутствует в определенной консультации.

with table1 as ( Copy the query of table1),
table3 as ( Copy the query of table3)
select *
from table1
join table3 on 
table1.id=table3.id 
order by 1

И финальная таблицавыглядит так:

ID    Species   Illness      Mucus  Noisy ... Limp  Itchy 
001   Dog       Asthma       1      1     ... 0     0
002   Cat       Osteoarth.   0      0     ... 1     0
...
003   Bird      Avian Pox    0      0     ... 0     1  

Ответы [ 2 ]

0 голосов
/ 14 марта 2019

В зависимости от того, как вы отображаете результаты запроса, вы можете рассмотреть другой подход, при котором вы получаете все флаги true / false для каждого тега в одном столбце JSONB, а не 350 динамических столбцов.

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

create table tags (id int, tag text);
create table consultations (id int, species text, illness text);
create table taggings (taggable_id int, tag_id int);

insert into tags 
  (id, tag)
values
  (1, 'Mucus'),
  (2, 'Noisy'),
  (3, 'Limp'),
  (4, 'Itchy'),
  (5, 'Locomotor'),
  (6, 'Respiratory');

insert into consultations
  (id, species, illness)
values 
  (1, 'Dog', 'Asthma'),
  (2, 'Cat', 'Osteoarthritis'),
  (3, 'Bird', 'Avian Pox');

insert into taggings 
  (taggable_id, tag_id)
values 
  (1, 1), (1, 2), (1, 6), -- the dog
  (2, 5), (2, 3), -- the cat 
  (3, 4); -- the bird

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

select c.id, c.species, c.illness, 
       (select jsonb_object_agg(t.tag, tg.taggable_id is not null)
        from tags t 
          left join taggings tg 
                 on tg.tag_id = t.id 
                and tg.taggable_id = c.id) as tags
from consultations c;

С приведенными выше примерами данных запрос возвращает:

id | species | illness        | tags                                                                                                    
---+---------+----------------+---------------------------------------------------------------------------------------------------------
 1 | Dog     | Asthma         | {"Limp": false, "Itchy": false, "Mucus": true, "Noisy": true, "Locomotor": false, "Respiratory": true}  
 2 | Cat     | Osteoarthritis | {"Limp": true, "Itchy": false, "Mucus": false, "Noisy": false, "Locomotor": true, "Respiratory": false} 
 3 | Bird    | Avian Pox      | {"Limp": false, "Itchy": true, "Mucus": false, "Noisy": false, "Locomotor": false, "Respiratory": false}

Альтернативный способ написания запроса - использовать боковое соединение:

select c.id, c.species, c.illness, ti.tags
from consultations c
  left join lateral (
    select jsonb_object_agg(t.tag, tg.taggable_Id is not null) as tags
    from tags t 
      left join taggings tg on tg.tag_id = t.id and tg.taggable_id = c.id
  ) as ti on true
0 голосов
/ 07 марта 2019

Я немного поэкспериментировал, и это то, что я придумал.

# Reading the data into a table

SELECT * INTO crosstab_test FROM 
(VALUES (20180101,'001','Dog','Asthma','Mucus'),
(20180101,'001','Dog','Asthma','Noisy'),
(20180101,'001','Dog','Asthma','Respiratory'),
(20180102,'002','Cat','Osteoarthritis','Locomotor'),
(20180102,'002','Cat','Osteoarthritis','Limp'),
(20180131, '003', 'Bird', 'Avian Pox','Itchy')) as a (date, id, species, illness, tag);

SELECT DISTINCT date, id, species, illness, mucus, noisy, locomotor, respiratory,  limp, itchy 
FROM 
(SELECT "date", id, species, illness
FROM crosstab_test) a
INNER JOIN             
(SELECT * FROM crosstab(
'SELECT id, tag, ''TRUE'' FROM crosstab_test ORDER BY 1,2,3',
'SELECT DISTINCT tag FROM crosstab_test ORDER BY 1')
as tabelle (id text, Itchy text, Limp text, Locomotor text, Mucus text, Noisy text, Respiratory text)) b
USING(id)
ORDER BY 1;


   date   | id  | species |    illness     | mucus | noisy | locomotor | respiratory | limp | itchy
----------+-----+---------+----------------+-------+-------+-----------+-------------+------+-------
 20180101 | 001 | Dog     | Asthma         | TRUE  | TRUE  |           | TRUE        |      |
 20180102 | 002 | Cat     | Osteoarthritis |       |       | TRUE      |             | TRUE |
 20180131 | 003 | Bird    | Avian Pox      |       |       |           |             |      | TRUE
(3 Zeilen)

Если вам не важен порядок столбцов, вы можете просто сделать SELECT DISTINCT * ...

Замена NULL s на FALSE, вероятно, будетнемного сложно, учитывая 350 тегов, которые вы говорите, у вас есть.Поэтому я предлагаю оставить их подальше.Если вы действительно хотите их, вы можете сделать SELECT DISTINCT date, id, species, illness, COALESCE(mucus, 'FALSE'), COALESCE(noisy, 'FALSE'),...

Горькая пилюля, которую вы должны будете проглотить, однако, это указать все 350 тегов в виде столбца с типом text в as the tabelle (id text, Itchy text, Limp text, Locomotor text, Mucus text, Noisy text, Respiratory text) -части оператора кросс-таблицы.Обязательно поместите их в правильном порядке, как определено 'SELECT DISTINCT tag FROM crosstab_test ORDER BY 1' также в перекрестной таблице.

Надеюсь, это то, что вы искали.

...