web-dev-qa-db-ja.com

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

テーブルが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='{}');

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

SQL Fiddle is here

10
Alexandros

正しい結果ですか?

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

隣接する重複を削除する

マニュアルで説明されています と同様に、次のものが必要です。

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

また、sorted配列を提供します-あなたがそれを望むと仮定して、あなたは明確にしませんでした。

私はあなたに会えます havesort() in your fiddle なので、これはあなたの質問のタイプミスかもしれません。

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_を追加しない限り結果はソートされません。並べ替えられた配列が必要な場合は、could_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 Fiddle あなたが提供したものに基づいています。

索引

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

_CREATE INDEX ON right2 (t1, arr);
_

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

9

私は本当にがっかりしています。これは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年間でソフトウェアを変更したので、両方のフォーマットを使用しています。

0
Alan Corey