row nameだけを使用するのではなく、tablefunc
を使用して複数の変数をピボットしましたか? ドキュメントノート :
「extra」列は、同じrow_name値を持つすべての行で同じであると想定されています。
ピボットしたい列を結合せずにこれを行う方法はわかりません(これにより、必要な速度が得られると確信しています)。これを行うための1つの可能な方法は、エンティティを数値にして、それをミリ秒としてローカルに追加することですが、これは続行するのに不安定な方法のようです。
この質問への回答で使用されるデータを編集しました: PostgreSQL Crosstab Query 。
CREATE TEMP TABLE t4 (
timeof timestamp
,entity character
,status integer
,ct integer);
INSERT INTO t4 VALUES
('2012-01-01', 'a', 1, 1)
,('2012-01-01', 'a', 0, 2)
,('2012-01-02', 'b', 1, 3)
,('2012-01-02', 'c', 0, 4);
SELECT * FROM crosstab(
'SELECT timeof, entity, status, ct
FROM t4
ORDER BY 1,2,3'
,$$VALUES (1::text), (0::text)$$)
AS ct ("Section" timestamp, "Attribute" character, "1" int, "0" int);
戻り値:
セクション|属性| 1 | 0 --------------------------- + ----------- + --- + --- 2012-01-01 00:00:00 | a | 1 | 2 2012-01-02 00:00:00 | b | 3 | 4
したがって、ドキュメントに記載されているように、extra列aka 'Attribute'は、各row nameaka 'Section '。したがって、それは報告します b 「エンティティ」にも 「c」 その「timeof」値の値。
望ましい出力:
Section | Attribute | 1 | 0
--------------------------+-----------+---+---
2012-01-01 00:00:00 | a | 1 | 2
2012-01-02 00:00:00 | b | 3 |
2012-01-02 00:00:00 | c | | 4
考えや参考資料はありますか?
もう少し背景:billionsの行に対してこれを行う必要がある可能性があり、このデータを長い形式で保存して、 tablefunc
を使用すると、通常の集計関数よりも長い形式からワイド形式に効率的に移行できます。
約300のエンティティに対して毎分約100の測定を行います。多くの場合、特定のエンティティの特定の秒に行われたさまざまな測定値を比較する必要があるため、非常に頻繁にワイドフォーマットに移行する必要があります。また、特定のエンティティで行われた測定は非常に変動します。
編集:私はこれに関するリソースを見つけました: http://www.postgresonline.com/journal/categories/24-tablefunc 。
クエリの問題は、b
およびc
同じタイムスタンプ2012-01-02 00:00:00
を共有し、クエリの最初にtimestamp
列timeof
があるため、太字で強調した場合でもb
とc
は、同じグループ2012-01-02 00:00:00
に含まれる追加の列です。 (マニュアルの引用) 以降、最初の(b
)のみが返されます:
row_name
列を最初にする必要があります。category
列とvalue
列は、この順序で最後の2列にする必要があります。row_name
とcategory
の間の列はすべて「追加」として扱われます。 「row_name
」の値が同じであるすべての行について、「追加」の列は同じであることが期待されます。
大胆な強調鉱山。
最初の2つの列の順序を逆にして、entity
を行名にすると、必要に応じて機能します。
SELECT * FROM crosstab(
'SELECT entity, timeof, status, ct
FROM t4
ORDER BY 1'
,'VALUES (1), (0)')
AS ct (
"Attribute" character
,"Section" timestamp
,"status_1" int
,"status_0" int);
entity
はもちろん一意でなければなりません。
row_name
最初extra
列nextcategory
(2番目のパラメーターで定義)およびvalue
last。追加の列は、各row_name
パーティションのfirst行から入力されます。他の行の値は無視されます。入力するrow_name
ごとに1列のみです。通常、それらは1つのrow_name
のすべての行で同じですが、それはあなた次第です。
SELECT localt, entity
, msrmnt01, msrmnt02, msrmnt03, msrmnt04, msrmnt05 -- , more?
FROM crosstab(
'SELECT dense_rank() OVER (ORDER BY localt, entity)::int AS row_name
, localt, entity -- additional columns
, msrmnt, val
FROM test
-- WHERE ??? -- instead of LIMIT at the end
ORDER BY localt, entity, msrmnt
-- LIMIT ???' -- instead of LIMIT at the end
, $$SELECT generate_series(1,5)$$) -- more?
AS ct (row_name int, localt timestamp, entity int
, msrmnt01 float8, msrmnt02 float8, msrmnt03 float8, msrmnt04 float8, msrmnt05 float8 -- , more?
)
LIMIT 1000 -- ??!!
テストのクエリがひどく実行されるのも不思議ではありません。テストセットアップには14M行あり、LIMIT 1000
を使用してそのほとんどを破棄する前に、allを処理します。結果セットを削減するには、WHERE条件またはLIMITをソースクエリに追加します。
さらに、作業するアレイは、それに加えて不必要に高価です。代わりに、dense_rank()を使用してサロゲート行名を生成します。
db <> fiddle ここ-テスト設定が簡単で行数が少ない。
私の元の質問では、サンプルデータにこれを使用する必要がありました。
CREATE TEMP TABLE t4 (
timeof date
,entity integer
,status integer
,ct integer);
INSERT INTO t4 VALUES
('2012-01-01', 1, 1, 1)
,('2012-01-01', 1, 0, 2)
,('2012-01-01', 3, 0, 3)
,('2012-01-02', 2, 1, 4)
,('2012-01-02', 3, 1, 5)
,('2012-01-02', 3, 0, 6);
これにより、timeofとentityの両方をピボットする必要があります。 tablefunc
はピボットに1つの列のみを使用するため、その列に両方の次元を詰め込む方法を見つける必要があります。 ( http://www.postgresonline.com/journal/categories/24-tablefunc )。そのリンクの例のように、私は配列を使いました。
SELECT (timestamp 'Epoch' + row_name[1] * INTERVAL '1 second')::date
as localt,
row_name[2] As entity, status1, status0
FROM crosstab('SELECT ARRAY[extract(Epoch from timeof), entity] as row_name,
status, ct
FROM t4
ORDER BY timeof, entity, status'
,$$VALUES (1::text), (0::text)$$)
as ct (row_name integer[], status1 int, status0 int)
FWIW、私は文字配列を使用してみましたが、これまでのところ、これは私のセットアップの方が速いようです。 9.2.3 Postgresql。
これが結果であり、望ましい出力です。
localt | entity | status1 | status0
--------------------------+---------+--------
2012-01-01 | 1 | 1 | 2
2012-01-01 | 3 | | 3
2012-01-02 | 2 | 4 |
2012-01-02 | 3 | 5 | 6
これがはるかに大きなデータセットでどのように機能するか知りたいので、後日報告します。
わかりましたので、これを自分のユースケースに近いテーブルで実行しました。私が間違っているか、クロス集計が私の使用に適していません。
最初に私はいくつかの同様のデータを作りました:
CREATE TABLE public.test (
id serial primary key,
msrmnt integer,
entity integer,
localt timestamp,
val double precision
);
CREATE INDEX ix_test_msrmnt
ON public.test (msrmnt);
CREATE INDEX ix_public_test_201201_entity
ON public.test (entity);
CREATE INDEX ix_public_test_201201_localt
ON public.test (localt);
insert into public.test (msrmnt, entity, localt, val)
select *
from(
SELECT msrmnt, entity, localt, random() as val
FROM generate_series('2012-01-01'::timestamp, '2012-01-01 23:59:00'::timestamp, interval '1 minutes') as localt
join
(select *
FROM generate_series(1, 50, 1) as msrmnt) as msrmnt
on 1=1
join
(select *
FROM generate_series(1, 200, 1) as entity) as entity
on 1=1) as data;
次に、クロス集計コードを数回実行しました。
explain analyze
SELECT (timestamp 'Epoch' + row_name[1] * INTERVAL '1 second')::date As localt, row_name[2] as entity
,msrmnt01,msrmnt02,msrmnt03,msrmnt04,msrmnt05,msrmnt06,msrmnt07,msrmnt08,msrmnt09,msrmnt10
,msrmnt11,msrmnt12,msrmnt13,msrmnt14,msrmnt15,msrmnt16,msrmnt17,msrmnt18,msrmnt19,msrmnt20
,msrmnt21,msrmnt22,msrmnt23,msrmnt24,msrmnt25,msrmnt26,msrmnt27,msrmnt28,msrmnt29,msrmnt30
,msrmnt31,msrmnt32,msrmnt33,msrmnt34,msrmnt35,msrmnt36,msrmnt37,msrmnt38,msrmnt39,msrmnt40
,msrmnt41,msrmnt42,msrmnt43,msrmnt44,msrmnt45,msrmnt46,msrmnt47,msrmnt48,msrmnt49,msrmnt50
FROM crosstab('SELECT ARRAY[extract(Epoch from localt), entity] as row_name, msrmnt, val
FROM public.test
ORDER BY localt, entity, msrmnt',$$VALUES ( 1::text),( 2::text),( 3::text),( 4::text),( 5::text),( 6::text),( 7::text),( 8::text),( 9::text),(10::text)
,(11::text),(12::text),(13::text),(14::text),(15::text),(16::text),(17::text),(18::text),(19::text),(20::text)
,(21::text),(22::text),(23::text),(24::text),(25::text),(26::text),(27::text),(28::text),(29::text),(30::text)
,(31::text),(32::text),(33::text),(34::text),(35::text),(36::text),(37::text),(38::text),(39::text),(40::text)
,(41::text),(42::text),(43::text),(44::text),(45::text),(46::text),(47::text),(48::text),(49::text),(50::text)$$)
as ct (row_name integer[],msrmnt01 double precision, msrmnt02 double precision,msrmnt03 double precision, msrmnt04 double precision,msrmnt05 double precision,
msrmnt06 double precision,msrmnt07 double precision, msrmnt08 double precision,msrmnt09 double precision, msrmnt10 double precision
,msrmnt11 double precision, msrmnt12 double precision,msrmnt13 double precision, msrmnt14 double precision,msrmnt15 double precision,
msrmnt16 double precision,msrmnt17 double precision, msrmnt18 double precision,msrmnt19 double precision, msrmnt20 double precision
,msrmnt21 double precision, msrmnt22 double precision,msrmnt23 double precision, msrmnt24 double precision,msrmnt25 double precision,
msrmnt26 double precision,msrmnt27 double precision, msrmnt28 double precision,msrmnt29 double precision, msrmnt30 double precision
,msrmnt31 double precision, msrmnt32 double precision,msrmnt33 double precision, msrmnt34 double precision,msrmnt35 double precision,
msrmnt36 double precision,msrmnt37 double precision, msrmnt38 double precision,msrmnt39 double precision, msrmnt40 double precision
,msrmnt41 double precision, msrmnt42 double precision,msrmnt43 double precision, msrmnt44 double precision,msrmnt45 double precision,
msrmnt46 double precision,msrmnt47 double precision, msrmnt48 double precision,msrmnt49 double precision, msrmnt50 double precision)
limit 1000
3回目の試行でこれを取得する:
QUERY PLAN
Limit (cost=0.00..20.00 rows=1000 width=432) (actual time=110236.673..110237.667 rows=1000 loops=1)
-> Function Scan on crosstab ct (cost=0.00..20.00 rows=1000 width=432) (actual time=110236.672..110237.598 rows=1000 loops=1)
Total runtime: 110699.598 ms
次に、標準ソリューションを数回実行しました。
explain analyze
select localt, entity,
max(case when msrmnt = 1 then val else null end) as msrmnt01
,max(case when msrmnt = 2 then val else null end) as msrmnt02
,max(case when msrmnt = 3 then val else null end) as msrmnt03
,max(case when msrmnt = 4 then val else null end) as msrmnt04
,max(case when msrmnt = 5 then val else null end) as msrmnt05
,max(case when msrmnt = 6 then val else null end) as msrmnt06
,max(case when msrmnt = 7 then val else null end) as msrmnt07
,max(case when msrmnt = 8 then val else null end) as msrmnt08
,max(case when msrmnt = 9 then val else null end) as msrmnt09
,max(case when msrmnt = 10 then val else null end) as msrmnt10
,max(case when msrmnt = 11 then val else null end) as msrmnt11
,max(case when msrmnt = 12 then val else null end) as msrmnt12
,max(case when msrmnt = 13 then val else null end) as msrmnt13
,max(case when msrmnt = 14 then val else null end) as msrmnt14
,max(case when msrmnt = 15 then val else null end) as msrmnt15
,max(case when msrmnt = 16 then val else null end) as msrmnt16
,max(case when msrmnt = 17 then val else null end) as msrmnt17
,max(case when msrmnt = 18 then val else null end) as msrmnt18
,max(case when msrmnt = 19 then val else null end) as msrmnt19
,max(case when msrmnt = 20 then val else null end) as msrmnt20
,max(case when msrmnt = 21 then val else null end) as msrmnt21
,max(case when msrmnt = 22 then val else null end) as msrmnt22
,max(case when msrmnt = 23 then val else null end) as msrmnt23
,max(case when msrmnt = 24 then val else null end) as msrmnt24
,max(case when msrmnt = 25 then val else null end) as msrmnt25
,max(case when msrmnt = 26 then val else null end) as msrmnt26
,max(case when msrmnt = 27 then val else null end) as msrmnt27
,max(case when msrmnt = 28 then val else null end) as msrmnt28
,max(case when msrmnt = 29 then val else null end) as msrmnt29
,max(case when msrmnt = 30 then val else null end) as msrmnt30
,max(case when msrmnt = 31 then val else null end) as msrmnt31
,max(case when msrmnt = 32 then val else null end) as msrmnt32
,max(case when msrmnt = 33 then val else null end) as msrmnt33
,max(case when msrmnt = 34 then val else null end) as msrmnt34
,max(case when msrmnt = 35 then val else null end) as msrmnt35
,max(case when msrmnt = 36 then val else null end) as msrmnt36
,max(case when msrmnt = 37 then val else null end) as msrmnt37
,max(case when msrmnt = 38 then val else null end) as msrmnt38
,max(case when msrmnt = 39 then val else null end) as msrmnt39
,max(case when msrmnt = 40 then val else null end) as msrmnt40
,max(case when msrmnt = 41 then val else null end) as msrmnt41
,max(case when msrmnt = 42 then val else null end) as msrmnt42
,max(case when msrmnt = 43 then val else null end) as msrmnt43
,max(case when msrmnt = 44 then val else null end) as msrmnt44
,max(case when msrmnt = 45 then val else null end) as msrmnt45
,max(case when msrmnt = 46 then val else null end) as msrmnt46
,max(case when msrmnt = 47 then val else null end) as msrmnt47
,max(case when msrmnt = 48 then val else null end) as msrmnt48
,max(case when msrmnt = 49 then val else null end) as msrmnt49
,max(case when msrmnt = 50 then val else null end) as msrmnt50
from sample
group by localt, entity
limit 1000
3回目の試行でこれを取得する:
QUERY PLAN
Limit (cost=2257339.69..2270224.77 rows=1000 width=24) (actual time=19795.984..20090.626 rows=1000 loops=1)
-> GroupAggregate (cost=2257339.69..5968242.35 rows=288000 width=24) (actual time=19795.983..20090.496 rows=1000 loops=1)
-> Sort (cost=2257339.69..2293339.91 rows=14400088 width=24) (actual time=19795.626..19808.820 rows=50001 loops=1)
Sort Key: localt
Sort Method: external merge Disk: 478568kB
-> Seq Scan on sample (cost=0.00..249883.88 rows=14400088 width=24) (actual time=0.013..2245.247 rows=14400000 loops=1)
Total runtime: 20197.565 ms
したがって、私の場合、これまでのところクロス集計は解決策ではないようです。そして、これは私が複数年を過ごすことになるちょうど1日です。実際、エンティティに対して行われる測定は可変であり、新しいものが導入されるという事実にもかかわらず、私はおそらくワイドフォーマットの(正規化されていない)テーブルを使用する必要がありますが、ここでは説明しません。
Postgres 9.2.3を使用した設定の一部を以下に示します。
name setting
max_connections 100
shared_buffers 2097152
effective_cache_size 6291456
maintenance_work_mem 1048576
work_mem 262144