検索する必要のあるテーブルには、以下に示すnumrange値の配列が含まれています。
_CREATE TABLE data ( sensor varchar(25), ranges numrange[] );
INSERT INTO data VALUES
('sensor0','{"[872985609.0,873017999.0]","[873021600.0,873035999.0]","[873039600.0,873072070.0]"}'::numrange[])
, ('sensor1','{"[872929250.000000,872985609.000000]"}'::numrange[]);
_
ranges
を使用すると、指定した範囲と重複するANY
の要素を含む行を簡単に検索できます。
_SELECT * FROM data WHERE '[873021700,873021800]'::numrange && ANY (ranges)
_
テーブルからフィールドを返すSELECTステートメントと、指定された範囲と重複する配列のonly要素が必要です。
_SELECT sensor,[array of overlapping numranges] FROM data WHERE '[873021700,873021800]'::numrange && ANY (ranges)
_
その結果:
_sensor ranges
sensor0 {"[873021600.0,873035999.0]"}
_
これは、サブセレクトで配列をunnest()
ingせずに可能ですか?そうでない場合、配列が潜在的に非常に大きい場合、unnest()
への効率的な方法は何でしょうか?
これは必要に応じて機能します。
SELECT d.sensor, r.overlapping_ranges
FROM data d
JOIN LATERAL (
SELECT array_agg(range) AS overlapping_ranges
FROM unnest(d.ranges) range
WHERE range && '[873021700,873021800]'::numrange
) r ON overlapping_ranges IS NOT NULL;
LATERAL
について:
大きなテーブルの場合、範囲配列の代わりに個別のranges
テーブル(行ごとに1つの範囲)を使用して設計を正規化する方がはるかに効率的です。そのためにGistインデックスを使用できます。
コメントで言及したような巨大なテーブル(10億行)の場合、サイズと BRIN index に最適化された別のranges
テーブルを検討します。それと一緒に行きます。
仮定:
bigint
に収まります。これは、保存するのにかなり安価です。下記参照。範囲タイプの演算子クラス Postgres 9.5に同梱されているのはrange_inclusion_ops
で、これはオーバーラップ演算子&&
をサポートしています。
ディスクスペースをさらに最適化するには、two bigint
numbers(数値に1000000を掛けた値)を保存して機能させるだけです。 BRINインデックス。基本的にこのように:
CREATE TABLE sensors (
sensor_id serial PRIMARY KEY
, sensor text NOT NULL);
CREATE TABLE ranges (
sensor_id int NOT NULL REFERENCES sensors
, range_low bigint NOT NULL
, range_hi bigint NOT NULL
);
INSERT INTO sensors (sensor) VALUES ('sensor1');
INSERT INTO ranges (sensor_id, range_low, range_hi) VALUES
(1, 872985609.0 * 1000000, 873017999.0 * 1000000) -- scaled
, (1, 872929250.000000 * 1000000, 872985609.000000 * 1000000);
CREATE INDEX ranges_brin_idx ON ranges USING BRIN (int8range(range_low, range_hi, '[]'));
Queryこれまでと同じ結果を得るには:
SELECT s.sensor, r.ranges
FROM (
SELECT sensor_id
, array_agg(numrange(range_low * .000001, range_hi * .000001, '[]')) AS ranges
FROM ranges
WHERE int8range(range_low, range_hi, '[]')
&& '[873021700000000,873021800000000]'::int8range -- scaled as well
GROUP BY sensor_id
) r
JOIN sensors s USING (sensor_id);
bigint
とnumrange
のストレージサイズ15桁の精度を持つnumrange
はディスク上で32バイトを占め、結果として1行あたり64バイトになります(さらに、int列、行ヘッダー、およびアイテムポインター)。
同じように2つのbigint
列(2 x 8バイト)を使用すると、合計52バイトになります。テーブルを約12 GB小さくにします。インデックスのサイズは同じです。
自分で見て:
SELECT pg_column_size((1::bigint, '[873021700.123456,873021800.123456]'::numrange))
, pg_column_size((1::bigint, 873021700123456::bigint, 873021700123456::bigint));
行サイズの詳細な説明: