Univot и PostgreSQL

Есть ли в PostgreSQL не эквивалентная функция?

Ответ 1

Создайте примерную таблицу:

CREATE TEMP TABLE foo (id int, a text, b text, c text);
INSERT INTO foo VALUES (1, 'ant', 'cat', 'chimp'), (2, 'grape', 'mint', 'basil');

С помощью UNION ALL вы можете "отключить" или "uncrosstab":

SELECT id,
       'a' AS colname,
       a AS thing
FROM foo
UNION ALL
SELECT id,
       'b' AS colname, 
       b AS thing
FROM foo
UNION ALL
SELECT id, 
       'c' AS colname,
       c AS thing
FROM foo
ORDER BY id;

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

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

Вместо этого используйте:

SELECT id,
       unnest(array['a', 'b', 'c']) AS colname,
       unnest(array[a, b, c]) AS thing
FROM foo
ORDER BY id;

Это проще написать, и он будет сканировать только один раз.

array[a, b, c] возвращает объект массива со значениями a, b и c в качестве его элементов. unnest(array[a, b, c]) разбивает результаты на одну строку для каждого из элементов массива.

Надеюсь, что это поможет!

Ответ 2

Вы можете использовать VALUES() и JOIN LATERAL, чтобы отключить столбцы.

Пример данных:

CREATE TABLE test(id int, a INT, b INT, c INT);
INSERT INTO test(id,a,b,c) VALUES (1,11,12,13),(2,21,22,23),(3,31,32,33);

запрос:

SELECT t.id, s.col_name, s.col_value
FROM test t
JOIN LATERAL(VALUES('a',t.a),('b',t.b),('c',t.c)) s(col_name, col_value) ON TRUE;

Демоверсия DBFiddle

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

EDIT

Используя предложение Зак :

SELECT t.id, col_name, col_value
FROM test t
CROSS JOIN LATERAL (VALUES('a', t.a),('b', t.b),('c',t.c)) s(col_name, col_value);

<=>

SELECT t.id, col_name, col_value
FROM test t
,LATERAL (VALUES('a', t.a),('b', t.b),('c',t.c)) s(col_name, col_value);

db & lt;> демонстрация скрипки

Ответ 3

FYI для тех из нас, кто ищет, как отключиться в RedShift.

Решение длинной формы, данное Stew, похоже, является единственным способом достижения этого.

https://forums.aws.amazon.com/thread.jspa?threadID=126369


Для тех, кто не видит его, вот текст, вставленный ниже...

У нас нет встроенных функций, которые будут выполнять поворот или разворот. Однако, вы всегда можете написать SQL для этого.

create table sales (regionid integer, q1 integer, q2 integer, q3 integer, q4 integer);
insert into sales values (1,10,12,14,16), (2,20,22,24,26);

select * from sales order by regionid;

regionid | q1 | q2 | q3 | q4
----------+----+----+----+----
1 | 10 | 12 | 14 | 16
2 | 20 | 22 | 24 | 26

(2 rows)

сводный запрос

create table sales_pivoted (regionid, quarter, sales)
as
select regionid, 'Q1', q1 from sales
UNION ALL
select regionid, 'Q2', q2 from sales
UNION ALL
select regionid, 'Q3', q3 from sales
UNION ALL
select regionid, 'Q4', q4 from sales
;

select * from sales_pivoted order by regionid, quarter;

regionid | quarter | sales 
----------+---------+-------
1 | Q1 | 10
1 | Q2 | 12
1 | Q3 | 14
1 | Q4 | 16
2 | Q1 | 20
2 | Q2 | 22
2 | Q3 | 24
2 | Q4 | 26
(8 rows)

запрос о неповторимости

select regionid, sum(Q1) as Q1, sum(Q2) as Q2, sum(Q3) as Q3, sum(Q4) as Q4
from
(select regionid, 
case quarter when 'Q1' then sales else 0 end as Q1,
case quarter when 'Q2' then sales else 0 end as Q2,
case quarter when 'Q3' then sales else 0 end as Q3,
case quarter when 'Q4' then sales else 0 end as Q4
from sales_pivoted)

group by regionid
order by regionid;
regionid | q1 | q2 | q3 | q4 
----------+----+----+----+----
1 | 10 | 12 | 14 | 16
2 | 20 | 22 | 24 | 26
(2 rows)

Надеюсь, что это поможет, Нил

Ответ 4

Я написал ужасную функцию univot для PostgreSQL. Он довольно медленный, но он, по крайней мере, возвращает результаты, как вы ожидали бы от операции "нет".

https://cgsrv1.arrc.csiro.au/blog/2010/05/14/unpivotuncrosstab-in-postgresql/

Надеюсь, вы сможете найти это полезным.

Ответ 5

Извлечение слегка измененного содержимого из ссылки в комментарии из @a_horse_with_no_name в ответ, потому что это работает:

Установка Hstore
Если у вас не установлен hstore и вы используете PostgreSQL 9. 1+, вы можете использовать удобный

CREATE EXTENSION hstore;

Для более низких версий найдите файл hstore.sql в share/contrib и запустите его в своей базе данных.

Предполагая, что ваша исходная таблица (например, широкие данные) имеет один столбец "id" с именем id_field и любое количество столбцов "value" одного и того же типа, из приведенного ниже создастся неповращенное представление этой таблицы.

CREATE VIEW vw_unpivot 
AS 
SELECT id_field, (h).key AS column_name, (h).value AS column_value
  FROM (SELECT id_field, each(hstore(foo) - 'id_field'::text) AS h
          FROM zcta5 as foo  
       ) AS unpiv ; 

Это работает с любым количеством столбцов 'value'. Все полученные значения будут текстовыми, если вы не произвели приведение, например, (h).value::numeric.