4

conttagtable(t) とcontfloattable(cf)の 2 つのテーブルがあります。T には約 43k 行あります。CFは90億以上あります。

tagindex両方のテーブルの列の両方のテーブルにインデックスを作成しました。この列は、 の一意の識別子および へのconttagtable外部キーと考えることができます。このデータは、あたかもaであるかのように、両方のテーブルの列によって論理的に関連付けられていますが、もう一方のテーブルに PK または外部キーを明示的に作成しませんでした。データはマイクロソフトのアクセス ダンプから取得されたものであり、tagindex が一意であると信頼できるかどうかわからなかったため、「一意性」は強制されません。conttagtableconfloattabletagindexconttagtable.tagindexPRIMARY KEYcontfloattable.tagindexFOREIGN KEY (tagindex) REFERENCES conttagtable(tagindex)

データ自体は非常に大きいです。

contfloattable各 の 15 分contfloattable.dateandtime間隔ごとに、 から任意に選択された単一の行を取得する必要がありますconttagtable.tagid。したがって、contfloattable特定のtagidに 30 分間にわたる 4000 個のサンプルがある場合、0 ~ 14 分の範囲のサンプルと 15 ~ 30 分の範囲のサンプルが必要です。15 分の範囲内の任意の 1 つのサンプルが許容されます。最初、最後、ランダム、何でも。

簡単に言えば、15 分ごとにサンプルを取得する必要がありますが、t.tagname ごとに 1 つのサンプルしか取得できません。現在、サンプルは 5 秒ごとに記録されており、データは 2 年間に及びます。これはビッグデータの問題であり、SQL に関しては私の頭をはるかに超えています。私がグーグルやSOで検索して試した時間間隔の解決策はすべて、実用的ではないほど長いクエリ時間を生み出しました。

  • インデックスは高速結合に十分ですか? (時間間隔部分を省略した場合に表示されます)
  • 他のインデックスを追加することでメリットがありますか?
  • 上記の目標を達成するための最良/最速のクエリは何ですか?

スキーマといくつかのサンプル データを含む SQLFiddle を次に示します: http://sqlfiddle.com/#!1/c7d2f/2

スキーマ:

        Table "public.conttagtable" (t)
   Column    |  Type   | Modifiers
-------------+---------+-----------
 tagname     | text    |
 tagindex    | integer |
 tagtype     | integer |
 tagdatatype | integer |
Indexes:
    "tagindex" btree (tagindex)


             Table "public.contfloattable" (CF)
   Column    |            Type             | Modifiers
-------------+-----------------------------+-----------
 dateandtime | timestamp without time zone |
 millitm     | integer                     |
 tagindex    | integer                     |
 Val         | double precision            |
 status      | text                        |
 marker      | text                        |
Indexes:
    "tagindex_contfloat" btree (tagindex)

私が見たい出力は次のようなものです:

cf.dateandtime      |cf."Val"|cf.status|t.tagname
--------------------------------------------------
2012-11-16 00:00:02  45       S         SuperAlpha
2012-11-16 00:00:02  45       S         SuperBeta
2012-11-16 00:00:02  45       S         SuperGamma
2012-11-16 00:00:02  45       S         SuperDelta
2012-11-16 00:15:02  45       S         SuperAlpha
2012-11-16 00:15:02  45       S         SuperBeta
2012-11-16 00:15:02  45       S         SuperGamma
2012-11-16 00:15:02  45       S         SuperDelta
2012-11-16 00:30:02  45       S         SuperAlpha
2012-11-16 00:30:02  45       S         SuperBeta
2012-11-16 00:30:02  45       S         SuperGamma
2012-11-16 00:30:02  45       S         SuperDelta
2012-11-16 00:45:02  42       S         SuperAlpha

...などなど...

Clodoaldo が示唆しているように、これは私の最新の試みですが、スピードアップするための提案はありますか?

with i as (
    select cf.tagindex, min(dateandtime) dateandtime
    from contfloattable cf
    group by
        floor(extract(epoch from dateandtime) / 60 / 15),
        cf.tagindex
)
select cf.dateandtime, cf."Val", cf.status, t.tagname
from
    contfloattable cf
    inner join
    conttagtable t on cf.tagindex = t.tagindex
    inner join
    i on i.tagindex = cf.tagindex and i.dateandtime = cf.dateandtime
order by floor(extract(epoch from cf.dateandtime) / 60 / 15), cf.tagindex

上記のクエリプラン: http://explain.depesz.com/s/loR

4

2 に答える 2

2

15 分間隔の場合:

with i as (
    select cf.tagindex, min(dateandtime) dateandtime
    from contfloattable cf
    group by
        floor(extract(epoch from dateandtime) / 60 / 15),
        cf.tagindex
)
select cf.dateandtime, cf."Val", cf.status, t.tagname
from
    contfloattable cf
    inner join
    conttagtable t on cf.tagindex = t.tagindex
    inner join
    i on i.tagindex = cf.tagindex and i.dateandtime = cf.dateandtime
order by cf.dateandtime, t.tagname

このクエリの Explain 出力を表示して (機能する場合)、最適化を試みることができます。この回答に投稿できます。

出力の説明

"Sort  (cost=15102462177.06..15263487805.24 rows=64410251271 width=57)"
"  Sort Key: cf.dateandtime, t.tagname"
"  CTE i"
"    ->  HashAggregate  (cost=49093252.56..49481978.32 rows=19436288 width=12)"
"          ->  Seq Scan on contfloattable cf  (cost=0.00..38528881.68 rows=1408582784 width=12)"
"  ->  Hash Join  (cost=270117658.06..1067549320.69 rows=64410251271 width=57)"
"        Hash Cond: (cf.tagindex = t.tagindex)"
"        ->  Merge Join  (cost=270117116.39..298434544.23 rows=1408582784 width=25)"
"              Merge Cond: ((i.tagindex = cf.tagindex) AND (i.dateandtime = cf.dateandtime))"
"              ->  Sort  (cost=2741707.02..2790297.74 rows=19436288 width=12)"
"                    Sort Key: i.tagindex, i.dateandtime"
"                    ->  CTE Scan on i  (cost=0.00..388725.76 rows=19436288 width=12)"
"              ->  Materialize  (cost=267375409.37..274418323.29 rows=1408582784 width=21)"
"                    ->  Sort  (cost=267375409.37..270896866.33 rows=1408582784 width=21)"
"                          Sort Key: cf.tagindex, cf.dateandtime"
"                          ->  Seq Scan on contfloattable cf  (cost=0.00..24443053.84 rows=1408582784 width=21)"
"        ->  Hash  (cost=335.74..335.74 rows=16474 width=44)"
"              ->  Seq Scan on conttagtable t  (cost=0.00..335.74 rows=16474 width=44)"

このインデックスが必要なようです:

create index cf_tag_datetime on contfloattable (tagindex, dateandtime)

analyze作成したら実行します。大きなテーブルのインデックスは、変更のたびに更新する必要があるため、データの変更 (挿入など) のパフォーマンスに大きな影響を与えることに注意してください。

アップデート

cf_tag_datetime インデックス (tagindex,dateandtime) を追加しました。新しい説明は次のとおりです。

"Sort  (cost=15349296514.90..15512953953.25 rows=65462975340 width=57)"
"  Sort Key: cf.dateandtime, t.tagname"
"  CTE i"
"    ->  HashAggregate  (cost=49093252.56..49490287.76 rows=19851760 width=12)"
"          ->  Seq Scan on contfloattable cf  (cost=0.00..38528881.68 rows=1408582784 width=12)"
"  ->  Hash Join  (cost=270179293.86..1078141313.22 rows=65462975340 width=57)"
"        Hash Cond: (cf.tagindex = t.tagindex)"
"        ->  Merge Join  (cost=270178752.20..298499296.08 rows=1408582784 width=25)"
"              Merge Cond: ((i.tagindex = cf.tagindex) AND (i.dateandtime = cf.dateandtime))"
"              ->  Sort  (cost=2803342.82..2852972.22 rows=19851760 width=12)"
"                    Sort Key: i.tagindex, i.dateandtime"
"                    ->  CTE Scan on i  (cost=0.00..397035.20 rows=19851760 width=12)"
"              ->  Materialize  (cost=267375409.37..274418323.29 rows=1408582784 width=21)"
"                    ->  Sort  (cost=267375409.37..270896866.33 rows=1408582784 width=21)"
"                          Sort Key: cf.tagindex, cf.dateandtime"
"                          ->  Seq Scan on contfloattable cf  (cost=0.00..24443053.84 rows=1408582784 width=21)"
"        ->  Hash  (cost=335.74..335.74 rows=16474 width=44)"
"              ->  Seq Scan on conttagtable t  (cost=0.00..335.74 rows=16474 width=44)"

時間内に上がったようです:(ただし、order by句を削除すると(正確には必要なものではありませんが、機能します)、これが起こります。大幅な削減:

"Hash Join  (cost=319669581.62..1127631600.98 rows=65462975340 width=57)"
"  Hash Cond: (cf.tagindex = t.tagindex)"
"  CTE i"
"    ->  HashAggregate  (cost=49093252.56..49490287.76 rows=19851760 width=12)"
"          ->  Seq Scan on contfloattable cf  (cost=0.00..38528881.68 rows=1408582784 width=12)"
"  ->  Merge Join  (cost=270178752.20..298499296.08 rows=1408582784 width=25)"
"        Merge Cond: ((i.tagindex = cf.tagindex) AND (i.dateandtime = cf.dateandtime))"
"        ->  Sort  (cost=2803342.82..2852972.22 rows=19851760 width=12)"
"              Sort Key: i.tagindex, i.dateandtime"
"              ->  CTE Scan on i  (cost=0.00..397035.20 rows=19851760 width=12)"
"        ->  Materialize  (cost=267375409.37..274418323.29 rows=1408582784 width=21)"
"              ->  Sort  (cost=267375409.37..270896866.33 rows=1408582784 width=21)"
"                    Sort Key: cf.tagindex, cf.dateandtime"
"                    ->  Seq Scan on contfloattable cf  (cost=0.00..24443053.84 rows=1408582784 width=21)"
"  ->  Hash  (cost=335.74..335.74 rows=16474 width=44)"
"        ->  Seq Scan on conttagtable t  (cost=0.00..335.74 rows=16474 width=44)"

私はまだこのインデックスを試していません...そうします。待機する。

Merge Joinもう一度見てみると、逆インデックスは、ファイナルだけでなく、ファイナルでも使用できるため、さらに優れていると思いますSort

create index cf_tag_datetime on contfloattable (dateandtime, tagindex)
于 2012-10-16T21:23:42.390 に答える
1

ここに別の定式化があります。それが完全なデータセットでどのようにスケーリングされるか、非常に興味があります。最初にこのインデックスを作成します。

CREATE INDEX contfloattable_tag_and_timeseg
ON contfloattable(tagindex, (floor(extract(epoch FROM dateandtime) / 60 / 15) ));

次に、work_mem余裕がある限りこれを実行します。

SELECT 
  (first_value(x) OVER (PARTITION BY x.tagindex, floor(extract(epoch FROM x.dateandtime) / 60 / 15))).*,
  (SELECT t.tagname FROM conttagtable t WHERE t.tagindex = x.tagindex) AS tagname
FROM contfloattable x ORDER BY dateandtime, tagname;

Sneaky Wombat : 完全なデータ セットに関する上記の SQL からの説明 (推奨インデックスなし): http://explain.depesz.com/s/kGo

contfloattable別の方法として、値を tuplestore に収集しJOIN、タグ名を取得するために ed を使用して、シーケンシャル パスを 1 回だけ実行する必要がある例を次に示します。多くのものが必要ですwork_mem:

SELECT cf.dateandtime, cf.dataVal, cf.status, t.tagname
FROM 
  (
    SELECT (first_value(x) OVER (PARTITION BY x.tagindex, floor(extract(epoch FROM x.dateandtime) / 60 / 15))).*
    FROM contfloattable x
  ) cf
  INNER JOIN
  conttagtable t ON cf.tagindex = t.tagindex
ORDER BY cf.dateandtime, t.tagname;

Sneaky Wombat : 完全なデータ セットに関する上記の SQL からの説明 (推奨インデックスなし): http://explain.depesz.com/s/57q

work_memそれが機能する場合は、クエリで余裕がある限り多くをスローする必要があります。システムの RAM について言及していませんが、かなりの量が必要です。試す:

SET work_mem = '500MB';

... 少なくとも 4GB の RAM があり、64 ビット CPU を使用している場合はそれ以上。繰り返しますが、完全なデータセットでどのように機能するかを知りたいです。

ところで、これらのクエリの正確性については、次のことをお勧めしALTER TABLE conttagtable ADD PRIMARY KEY (tagindex);ますDROP INDEX t_tagindex;。独自のインデックスを構築するため、しばらく時間がかかります。ここで言及されているクエリのほとんどは、t.tagindexが で一意であると想定してconttagtableおり、実際に強制する必要があります。ユニーク インデックスは、古い非ユニーク インデックスでは不可能な追加の最適化に使用t_tagindexでき、はるかに優れた統計推定値を生成します。

また、クエリ プランを比較するときcostは、実際の実行時間に厳密に比例するとは限らないことに注意してください。見積もりが良ければ、おおまかに相関するはずですが、見積もりはそれだけです。不適切な行数の見積もりやインデックスの選択性の見積もり、関係を推測するクエリ プランナーの能力の制限、予期しない相関関係、または のようなコスト パラメーターなどの理由により、高コストのプランが低コストと思われるプランよりも速く実行される場合がありrandom_page_costますseq_page_cost。実際のシステムと一致しません。

于 2012-10-17T02:05:00.077 に答える