PostgreSQL Crosstab Query
Czy ktoś wie jak tworzyć zapytania crosstab w PostgreSQL?
Na przykład mam następującą tabelę:
Section Status Count
A Active 1
A Inactive 2
B Active 4
B Inactive 5
Chciałbym, aby zapytanie zwróciło następujący crosstab:
Section Active Inactive
A 1 2
B 4 5
Czy to możliwe? 6 answers
Zainstaluj dodatkowy moduł tablefunc
raz NA bazę danych, która dostarcza funkcję crosstab()
. Od Postgres 9.1 można używać CREATE EXTENSION
za to:
CREATE EXTENSION IF NOT EXISTS tablefunc;
Ulepszony przypadek testowy
CREATE TABLE tbl (
section text
, status text
, ct integer -- "count" is a reserved word in standard SQL
);
INSERT INTO tbl VALUES
('A', 'Active', 1), ('A', 'Inactive', 2)
, ('B', 'Active', 4), ('B', 'Inactive', 5)
, ('C', 'Inactive', 7); -- ('C', 'Active') is missing
Prosta forma - nie nadaje się do brakujących atrybutów
crosstab(text)
z 1 parametr wejściowy:
SELECT *
FROM crosstab(
'SELECT section, status, ct
FROM tbl
ORDER BY 1,2' -- needs to be "ORDER BY 1,2" here
) AS ct ("Section" text, "Active" int, "Inactive" int);
Zwraca:
Section | Active | Inactive ---------+--------+---------- A | 1 | 2 B | 4 | 5 C | 7 | -- !!
- nie ma potrzeby odlewania i zmiany nazwy.
- zwróć uwagę na niepoprawny wynik dla
C
: wartość7
jest wypełniona dla pierwszej kolumny. Czasami takie zachowanie jest pożądane, ale nie w tym przypadku użycia. - prosta forma jest również ograniczona do dokładnie trzech kolumn w dostarczonym zapytaniu wejściowym: nazwa wiersza, Kategoria, wartość . Nie ma miejsca na dodatkowe kolumny jak w alternatywie 2-parametrowej poniżej.
Bezpieczna forma
crosstab(text, text)
z 2 parametry wejściowe:
SELECT *
FROM crosstab(
'SELECT section, status, ct
FROM tbl
ORDER BY 1,2' -- could also just be "ORDER BY 1" here
, $$VALUES ('Active'::text), ('Inactive')$$
) AS ct ("Section" text, "Active" int, "Inactive" int);
Zwraca:
Section | Active | Inactive ---------+--------+---------- A | 1 | 2 B | 4 | 5 C | | 7 -- !!
Zwróć uwagę na poprawny wynik dla
C
.-
drugi parametr może być dowolnym zapytaniem, które zwraca jeden wiersz na atrybut odpowiadający kolejności definicji kolumny na końcu. Często będziesz chciał odpytywać różne atrybuty z bazowej tabeli w następujący sposób:
To jest w instrukcji.'SELECT DISTINCT attribute FROM tbl ORDER BY 1'
Ponieważ musisz przeliterować wszystkie kolumny w liście definicji kolumn w każdym razie( z wyjątkiem wstępnie zdefiniowanych wariantów
crosstabN()
), zwykle bardziej wydajne jest dostarczenie krótkiej listy w wyrażeniuVALUES
, Jak pokazano:$$VALUES ('Active'::text), ('Inactive')$$)
Lub (nie w instrukcji):
$$SELECT unnest('{Active,Inactive}'::text[])$$ -- short syntax for long lists
Używałem notowania dolara aby ułatwić cytowanie.
Możesz nawet wypisywać kolumny z różne typy danych Z
crosstab(text, text)
- o ile tekstowa reprezentacja wartości kolumna jest poprawnym wejściem dla typu docelowego. W ten sposób możesz mieć atrybuty innego rodzaju i wyjściatext
,date
,numeric
itd. dla odpowiednich atrybutów. Na końcu rozdziału znajduje się przykład koducrosstab(text, text)
w instrukcji .
db fiddle tutaj
Zaawansowane przykłady
-
Pivot na wielu kolumnach za pomocą Tablefunc - również demonstrując wspomniane " extra kolumny "
\crosstabview
w psql
Postgres 9.6 dodano tę meta-komendę do domyślnego interaktywnego terminala psql. Możesz uruchomić zapytanie, którego chcesz użyć jako pierwszego parametru crosstab()
i podać je do \crosstabview
(natychmiast lub w następnym kroku). Like:
db=> SELECT section, status, ct FROM tbl \crosstabview
Podobny wynik jak wyżej, ale jest to funkcja reprezentacji na wyłącznie po stronie klienta. Wiersze wejściowe są traktowane nieco inaczej, dlatego ORDER BY
nie jest wymagane. Szczegóły dla \crosstabview
w instrukcji. na dole tej strony jest więcej przykładów kodu.
Pokrewna odpowiedź na dba.SE w 2011 roku został wybrany na członka zarządu psql.]}
Poprzednio zaakceptowane odpowiedź jest nieaktualna.
-
Wariant funkcji {[30] } jest przestarzały. Drugi parametr
integer
jest ignorowany. Cytuję: current manual :crosstab(text sql, int N)
...Przestarzała wersja
crosstab(text)
. Parametr {[34] } jest teraz ignorowany, ponieważ ilość kolumn wartości jest zawsze określana przez wywołujące zapytanie Niepotrzebne Odlewanie i zmiana nazwy.
Nie powiedzie się, jeśli wiersz nie mają wszystkich atrybutów. Zobacz bezpieczny wariant z dwoma parametrami wejściowymi powyżej, aby poprawnie obsłużyć brakujące atrybuty.
-
ORDER BY
jest wymagane w postaci jednoparametrowejcrosstab()
. Instrukcja:W praktyce zapytanie SQL powinno zawsze określać
ORDER BY 1,2
, aby zapewnić że wiersze wejściowe są prawidłowo uporządkowane
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-02-27 19:22:06
Możesz użyć funkcji crosstab()
z dodatkowego modułu tablefunc - który musisz zainstalować raz na bazę danych. Od PostgreSQL 9.1 można używać CREATE EXTENSION
za to:
CREATE EXTENSION tablefunc;
W Twoim przypadku wydaje mi się, że wyglądałoby to mniej więcej tak:
CREATE TABLE t (Section CHAR(1), Status VARCHAR(10), Count integer);
INSERT INTO t VALUES ('A', 'Active', 1);
INSERT INTO t VALUES ('A', 'Inactive', 2);
INSERT INTO t VALUES ('B', 'Active', 4);
INSERT INTO t VALUES ('B', 'Inactive', 5);
SELECT row_name AS Section,
category_1::integer AS Active,
category_2::integer AS Inactive
FROM crosstab('select section::text, status, count::text from t',2)
AS ct (row_name text, category_1 text, category_2 text);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-28 21:36:46
SELECT section,
SUM(CASE status WHEN 'Active' THEN count ELSE 0 END) AS active, --here you pivot each status value as a separate column explicitly
SUM(CASE status WHEN 'Inactive' THEN count ELSE 0 END) AS inactive --here you pivot each status value as a separate column explicitly
FROM t
GROUP BY section
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-28 12:23:42
Rozwiązanie z agregacją JSON:
CREATE TEMP TABLE t (
section text
, status text
, ct integer -- don't use "count" as column name.
);
INSERT INTO t VALUES
('A', 'Active', 1), ('A', 'Inactive', 2)
, ('B', 'Active', 4), ('B', 'Inactive', 5)
, ('C', 'Inactive', 7);
SELECT section,
(obj ->> 'Active')::int AS active,
(obj ->> 'Inactive')::int AS inactive
FROM (SELECT section, json_object_agg(status,ct) AS obj
FROM t
GROUP BY section
)X
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-24 12:31:41
Przykro mi, że to nie jest kompletne, ponieważ nie mogę go przetestować tutaj, ale może to cię podniesie we właściwym kierunku. Tłumaczę z czegoś, czego używam, co sprawia, że podobne zapytanie:
select mt.section, mt1.count as Active, mt2.count as Inactive
from mytable mt
left join (select section, count from mytable where status='Active')mt1
on mt.section = mt1.section
left join (select section, count from mytable where status='Inactive')mt2
on mt.section = mt2.section
group by mt.section,
mt1.count,
mt2.count
order by mt.section asc;
Kod, z którego pracuję to:
select m.typeID, m1.highBid, m2.lowAsk, m1.highBid - m2.lowAsk as diff, 100*(m1.highBid - m2.lowAsk)/m2.lowAsk as diffPercent
from mktTrades m
left join (select typeID,MAX(price) as highBid from mktTrades where bid=1 group by typeID)m1
on m.typeID = m1.typeID
left join (select typeID,MIN(price) as lowAsk from mktTrades where bid=0 group by typeID)m2
on m1.typeID = m2.typeID
group by m.typeID,
m1.highBid,
m2.lowAsk
order by diffPercent desc;
Który zwróci typeID, najwyższą cenę i najniższą cenę oraz różnicę między nimi (dodatnia różnica oznaczałaby, że coś można kupić za mniej niż można sprzedać).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-09 02:34:56
Crosstab
funkcja jest dostępna pod rozszerzeniem tablefunc
. Będziesz musiał utworzyć to rozszerzenie jeden raz dla bazy danych.
CREATE EXTENSION tablefunc
;
możesz użyć poniższego kodu, aby utworzyć tabelę przestawną za pomocą zakładki krzyżowej:
create table test_Crosstab( section text,
<br/>status text,
<br/>count numeric)
<br/>insert into test_Crosstab values ( 'A','Active',1)
<br/>,( 'A','Inactive',2)
<br/>,( 'B','Active',4)
<br/>,( 'B','Inactive',5)
select * from crosstab(
<br/>'select section
<br/>,status
<br/>,count
<br/>from test_crosstab'
<br/>)as ctab ("Section" text,"Active" numeric,"Inactive" numeric)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-12 12:00:18