PostgreSQLでクロス集計クエリを作成する方法を知っている人はいますか?
たとえば、次の表があります。
Section Status Count
A Active 1
A Inactive 2
B Active 4
B Inactive 5
クエリが次のクロス集計を返すようにします。
Section Active Inactive
A 1 2
B 4 5
これは可能ですか?
追加モジュールをインストールしますtablefunc
onceデータベースごとに、関数crosstab()
を提供します。 Postgres 9.1以降では、 CREATE EXTENSION
を使用できます。
CREATE EXTENSION IF NOT EXISTS tablefunc;
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
crosstab(text)
with1入力パラメーター:
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);
戻り値:
セクション|アクティブ|非アクティブ --------- + -------- + ---------- A | 1 | 2 B | 4 | 5 C | 7 | -!!
C
のincorrectの結果に注意してください:最初の列には値7
が入力されます。この動作が望ましい場合もありますが、このユースケースには適していません。crosstab(text, text)
with2入力パラメーター:
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);
戻り値:
セクション|アクティブ|非アクティブ --------- + -------- + ---------- A | 1 | 2 B | 4 | 5 C | | 7 -!!
C
の正しい結果に注意してください。
secondパラメーターは、最後に列定義の順序と一致する属性ごとに1つrowを返す任意のクエリにすることができます。多くの場合、次のように、基になるテーブルから個別の属性を照会する必要があります。
'SELECT DISTINCT attribute FROM tbl ORDER BY 1'
それはマニュアルにあります。
とにかく列定義リストのすべての列を綴る必要があるため(事前定義されたcrosstabN()
バリアントを除く)、通常、次のようにVALUES
式で短いリストを提供する方が効率的です。
$$VALUES ('Active'::text), ('Inactive')$$)
または(マニュアルにはありません):
$$SELECT unnest('{Active,Inactive}'::text[])$$ -- short syntax for long lists
dollar quoting を使用して、引用を簡単にしました。
値列のテキスト表現であれば、differentデータ型でcrosstab(text, text)
の列を出力することもできます。ターゲットタイプの有効な入力です。この方法では、異なる種類の属性を持ち、それぞれの属性に対してtext
、date
、numeric
などを出力できます。マニュアルの 章crosstab(text, text)
の最後にコード例があります 。
db <>フィドル ここ
Tablefuncを使用した複数列のピボット -前述の「追加列」のデモ
\crosstabview
Postgres9.6は、このメタコマンドをデフォルトのインタラクティブ端末 psql に追加しました。最初のcrosstab()
パラメーターとして使用するクエリを実行し、\crosstabview
に渡すことができます(すぐに、または次のステップで)。のような:
db=> SELECT section, status, ct FROM tbl \crosstabview
上記と同様の結果ですが、これはクライアント側の表現機能のみです。入力行の扱いが少し異なるため、ORDER BY
は不要です。マニュアルの \crosstabview
の詳細。 そのページの下部にさらにコード例があります。
DanielVérité(psql機能の作成者)によるdba.SEに関する関連回答:
以前に受け入れられた回答 は古くなっています。
関数crosstab(text, integer)
のバリアントは古くなっています。 2番目のinteger
パラメーターは無視されます。 currentマニュアルを引用します :
crosstab(text sql, int N)
...
crosstab(text)
の廃止バージョン。パラメータN
は、値列の数が常に呼び出し元のクエリによって決定されるため、無視されます
不必要なキャストと名前変更。
行にすべての属性がない場合、失敗します。欠落している属性を適切に処理するには、上記の2つの入力パラメーターを持つ安全なバリアントを参照してください。
ORDER BY
は、crosstab()
の1パラメーター形式で必要です。 マニュアル:
実際には、SQLクエリは常に
ORDER BY 1,2
を指定して、入力行が適切に順序付けられるようにする必要があります
追加モジュールtablefunc -のcrosstab()
関数を使用できます。これは、データベースごとにインストールする必要がありますonce。 PostgreSQL 9.1以降では、 CREATE EXTENSION
を使用できます:
CREATE EXTENSION tablefunc;
あなたの場合、私はそれが次のように見えると信じています:
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);
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
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
申し訳ありませんが、ここでテストすることはできないため、これは完了していませんが、正しい方向に進む可能性があります。同様のクエリを作成するために使用しているものから翻訳しています。
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;
私が作業しているコードは次のとおりです。
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;
typeID、最高値、最低値、および両者の差を返します(差が正の場合は、販売できる金額より少ない金額で購入できる可能性があることを意味します)。