5

PostgreSql8.4クエリで

explain analyze SELECT 
    max( kuupaev||kellaaeg ) as res
  from ALGSA 
  where laonr=1 and kuupaev <='9999-12-31' and 
     kuupaev||kellaaeg <= '9999-12-3123 59'

実行には3秒かかります:

"Aggregate  (cost=3164.49..3164.50 rows=1 width=10) (actual time=2714.269..2714.270 rows=1 loops=1)"
"  ->  Seq Scan on algsa  (cost=0.00..3110.04 rows=21778 width=10) (actual time=0.105..1418.743 rows=70708 loops=1)"
"        Filter: ((kuupaev <= '9999-12-31'::date) AND (laonr = 1::numeric) AND ((kuupaev || (kellaaeg)::text) <= '9999-12-3123 59'::text))"
"Total runtime: 2714.363 ms"

PostgreSQL 8.4.4で高速化する方法は?表の構成は以下のとおりです。algsaテーブルにはkuupaevのインデックスがあります。おそらくこれを使用できますか?または、クエリを変更して他のインデックスを追加して高速化することは可能ですか。テーブル内の既存の列は変更できません。

CREATE TABLE firma1.algsa
(
  id serial NOT NULL,
  laonr numeric(2,0),
  kuupaev date NOT NULL,
  kellaaeg character(5) NOT NULL DEFAULT ''::bpchar,
  ... other columns
  CONSTRAINT algsa_pkey PRIMARY KEY (id),
  CONSTRAINT algsa_id_check CHECK (id > 0)
)
);

CREATE INDEX algsa_kuupaev_idx  ON firma1.algsa  USING btree  (kuupaev);

アップデート

試してみましたanalyze verbose firma1.algsa;

INFO:  analyzing "firma1.algsa"
INFO:  "algsa": scanned 1640 of 1640 pages, containing 70708 live rows and 13 dead rows; 30000 rows in sample, 70708 estimated total rows
Query returned successfully with no result in 1185 ms.

しかし、クエリの実行時間はまだ2.7秒でした。

なぜあるのか30000 rows in sample 。多すぎませんか、これを減らすべきですか?

4

2 に答える 2

6

これは PostgreSQL の古いバージョンの既知の問題でしたが、8.4 までに解決されたようです。実際、8.0 のドキュメントには警告がありますが、8.1 のドキュメントにはありません。

したがって、少なくともこの理由でメジャー バージョンをアップグレードする必要はありません。ただし、数年分のバグ修正と微調整が欠けているため、現在の 8.4 シリーズ リリース 8.4.16 にアップグレードする必要があります。

ここでの本当の問題はmax、単純な値ではなく式で使用していて、その式の関数インデックスがないことです。

式にインデックスを作成することもできkuupaev||kellaaegますが、データ モデルに問題があると思われます。データ モデルを修正することで、より良い解決策があると思います。

kuupaevkuupäev、または日付のように見え、kellaaeg は時間かもしれません。その場合: 日付と時刻を結合するために連結 ( ||) 演算子を使用しないでください。使用間隔加算、例えばkuupaev + kellaaeg。代わりにchar、データ型を使用するtimeか、それが何を意味するか、および 24 時間に制限されているかどうかに応じてintervalCHECK制約を使用する必要があります。または、 (ローカル時間の場合) または(グローバル時間の場合)kellaaegタイプの 1 つのフィールドを使用して、結合された日付と時刻を格納することをお勧めします。timestamptimestamp with time zone

これを行うと、結合された列に単純なインデックスを作成し、 and を置き換えて、kellaaegそれをとりわけkuupaevand に使用できます。日付の部分だけ、または時刻の部分だけが必要な場合は、関数と関数を使用します。ドキュメントを参照してください。minmaxdate_truncextractdate_part

別の列と列が悪い考えである別の例については、この以前の回答を参照してください。datetime

9.2 へのアップグレードを計画する必要があります。8.4 から 9.2 へのアップグレード パスはそれほど大雑把ではありません。実際にはstandard_conforming_strings、デフォルトでの onの設定とbytea_outputからescapeへの変更に注意する必要がありますhex。どちらも、移行および移植作業中に 8.4 のデフォルトに戻すことができます。8.4 はサポートされなくなります。

于 2013-02-19T10:21:20.737 に答える
3

私の最初の本能は、インデックスを試すことです:

 create index algsa_laonr_kuupaev_kellaaeg_idx
 on ALGSA (laonr asc, (kuupaev||kellaaeg) desc)

...そしてクエリを次のように試してください:

 SELECT kuupaev||kellaaeg as res
 from ALGSA 
 where laonr=1 and
       kuupaev||kellaaeg <= '9999-12-3123 59'
 order by
       laonr asc,
       kuupaev||kellaaeg desc
 limit 1
于 2013-02-19T10:26:44.807 に答える