配列の効率的なマージ(重複の削除)


10

テーブルが2つleft2ありright2ます。両方のテーブルは大きくなります(1〜1000万行)。

CREATE TABLE left2(id INTEGER, t1 INTEGER, d INTEGER);
ALTER TABLE left2 ADD PRIMARY KEY (id,t1);

CREATE TABLE right2( t1 INTEGER, d INTEGER, arr INTEGER[] );
ALTER TABLE right2 ADD PRIMARY KEY(t1,d);

このタイプのクエリを実行します。

SELECT l.d + r.d,
       UNIQ(SORT((array_agg_mult(r.arr)))
FROM left2 l,
     right2 r
WHERE l.t1 = r.t1
GROUP BY l.d + r.d
ORDER BY l.d + r.d;

配列の集約には、次の関数を使用します。

CREATE AGGREGATE array_agg_mult(anyarray) (
SFUNC=array_cat,
STYPE=anyarray,
INITCOND='{}');

配列を連結した後UNIQintarrayモジュールの関数を使用します。これを行うより効率的な方法はありますか?arr(重複を削除して)マージを高速化するフィールドにインデックスはありますか?集約関数は重複を直接削除できますか?それが役立つ場合、元の配列はソートされていると見なされます(それらは一意です)。

SQLフィドルはここにあります


何百万もの行を一度にクエリしますか?結果をどうしますか?または、いくつかを選択する述語はありますか?right2.arr デモスキーマが示唆するようにNULLにすることができますか?結果としてソートされた配列が必要ですか?
Erwin Brandstetter、2015年

回答:


9

正しい結果ですか?

まず、正確さ。ユニークな要素の配列を作りたいですか?現在のクエリはそれを行いません。intarrayモジュールの関数uniq()は、次のことのみを約束します。

隣接する重複を削除する

同様のマニュアルで指示は、次のものが必要になります。

SELECT l.d + r.d, uniq(sort(array_agg_mult(r.arr)))
FROM   ...

また、並べ替えられた配列を提供します-あなたがそれを望んでいると仮定して、あなたは明確にしませんでした。

私はあなた sort()あなたのフィドル持っているのを見るので、これはあなたの質問の単なるタイプミスかもしれません。

Postgres 9.5

いずれにせよ、あなたがします愛する新しいPostgresの9.5(現在ベータ版)を。array_agg_mult()すぐに使用できる機能を提供し、はるかに高速です。

配列処理に関する他のパフォーマンスの改善もありました。

クエリ

の主な目的はarray_agg_mult()多次元配列を集約することですが、とにかく1次元配列のみを作成します。だから私は少なくともこの代替クエリを試します:

SELECT l.d + r.d AS d_sum, array_agg(DISTINCT elem) AS result_arr
FROM   left2  l
JOIN   right2 r USING (t1)
     , unnest(r.arr) elem
GROUP  BY 1
ORDER  BY 1;

これはあなたの質問にも対処します:

集約関数は重複を直接削除できますか?

はい、できDISTINCTます。しかしuniq()、これは整数配列用に最適化された整数配列よりも速くはありませんDISTINCTが、すべての適格なデータ型に対して一般的です。

intarrayモジュールは必要ありません。しかしながら、結果は必ずしもソートされません。PostgresはDISTINCT(IIRC)のさまざまなアルゴリズムを使用します。大きなセットは通常ハッシュされ、明示的にを追加しない限り結果はソートされませんORDER BY。並べ替えられた配列が必要な場合は、集約関数に直接追加できますORDER BY

array_agg(DISTINCT elem ORDER BY elem)

しかし、それは通常 、事前にソートされたデータをフィードするより遅くなりますarray_agg()(1つの大きなソートと多くの小さなソート)。だから私はサブクエリでソートしてから集計します:

SELECT d_sum, uniq(array_agg(elem)) AS result_arr
FROM  (
   SELECT l.d + r.d AS d_sum, elem
   FROM   left2  l
   JOIN   right2 r USING (t1)
        , unnest(r.arr) elem
   ORDER  BY 1, 2
   ) sub
GROUP  BY 1
ORDER  BY 1;

これは、Postgres 9.4での大まかなテストで最速のバリアントでした。

あなたが提供したものに基づく SQLフィドル

索引

ここにはインデックスの可能性はあまりありません。唯一のオプションは次のとおりです。

CREATE INDEX ON right2 (t1, arr);

これからインデックスのみのスキャンを取得する場合にのみ意味があります。これは、基になるテーブルright2がこれら2つの列だけよりも実質的に広く、設定がインデックスのみのスキャンに適格である場合に発生します。Postgres Wikiの詳細。


ありがとう+1。とにかく後でUNNESTする必要がありますが、配列内の重複を削除してからUNNESTの方が速いかどうかを確認したいと思います。
Alexandros、

0

私は本当にがっかりしています。これはMicrosoft Accessで簡単に実行できることです。「重複を削除する」クエリを作成してから、SQLを調べてどのように実行されているかを確認できます。見るにはWindowsマシンを起動する必要があります。クエリウィザードがそれを行います。

すべてのデータを1つのテーブルにロードしてから、SELECT DISTINCTを新しいテーブルに実行すると、うまくいくと思います。同時に、order by句を使用することもできます。なんとか一年前にやった、それはそれだろう。

2年分の温度データを組み合わせています。センサーは、冗長な保護手段として、毎分2つの同じデータポイントのコピーを送信します。ときどき1つはゴミ箱に落ちますが、私は1つだけ残しておきます。また、ファイル間で重複があります。

データが実行全体でまったく同じ形式である場合、UNIXマシンでは次のようなことができます

cat *.tab > points.txt
sort -n < points.txt > sorted.txt
uniq -u sorted.txt unique.txt

しかし、uniqは行を文字列として比較し、たとえば18.7000は18.7と同じではありません。2年間でソフトウェアを変更したので、両方のフォーマットを使用しています。


Postgresに失望しましたか?Accessには配列さえありますか?
ypercubeᵀᴹ

わかりませんが、重複を取り除くことができます。これは、データクレンジングでよくある問題です。個別の選択は十分に近いです。実世界の生データを常に制御できるとは限りません。
アランコーリー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.