2

サブクエリが原因で実行に時間がかかる(5分)postgrsqlクエリがあります。このクエリを拡張する方法を見つけたいと思います。

select v.id, v.pos, v.time,  v.status,  vi.name,vi.type,
        (select c.fullname
           from company c
          where vi.registered_owner_code = c.owcode ) AS registered_owner
       ,(select c.fullname
           from company c
         where vi.group_beneficial_owner_code=c.owcode) AS group_beneficial_owner
       ,(select c.fullname
           from company c
          where vi.operator_code = c.owcode ) AS operator
       ,(select c.fullname
           from company c
          where vi.manager_code = c.owcode ) AS manager
  from (car_pos v left join cars vi on v.id = vi.id)
 where age(now(), v.time::time with time zone) < '1 days'::interval
4

5 に答える 5

2

サブクエリのために私は思う

これは実際には推測ゲームではありません。pgadminまたはコンソールのすぐ下で、クエリ実行計画の説明を取得できます

http://www.pgadmin.org/docs/1.4/query.html

http://www.postgresql.org/docs/current/static/sql-explain.html

そうすれば、何が起こっているのか、何がそんなに時間がかかるのかがわかります。

分析後、インデックスを追加したり、何かを変更したりできますが、少なくとも何を変更する必要があるかがわかります。

于 2012-12-01T12:17:44.033 に答える
1

WHERE 条件ではインデックスを使用できません。インデックスを変更する必要があります。v.time は揮発性関数 (この場合は age()) 内にあってはなりません。

于 2012-12-01T12:19:57.157 に答える
1

3つの重要な成分:

  1. 相関サブクエリを廃止し、代わりに使用しJOINます-すでに述べた他の回答と同様です。

  2. WHEREでは、インデックスを利用できない列で式を使用しないでください。@フランクはすでにそれについて言及しています。最も基本的で安定した式のみが、クエリ プランナーがインデックスを使用するように書き換えることができます。書き直した様子をご覧ください。

  3. 適切なインデックスを作成します。

SELECT v.id, v.pos, v.time, v.status, c.name, c.type
      ,r.fullname AS registered_owner
      ,g.fullname AS group_beneficial_owner
      ,o.fullname AS operator
      ,m.fullname AS manager
FROM   car_pos v
LEFT   JOIN cars    c ON USING (id)
LEFT   JOIN company r ON r.owcode = c.registered_owner_code
LEFT   JOIN company g ON g.owcode = c.group_beneficial_owner_code
LEFT   JOIN company o ON o.owcode = c.operator_code
LEFT   JOIN company m ON m.owcode = c.manager_code
WHERE  v.time > (now() - interval '1 day');

cars.idとに一意のインデックスが必要ですcompany.owcode(主キーも機能します)。

そして、次のようなインデックスが必要ですcar_pos.time:

CREATE INDEX car_pos_time_idx ON car_pos (time DESC);

降順なしでも機能します。行数が多い(-> 大きなテーブル、大きなインデックス) 場合は、最近の履歴のみをカバーする部分インデックスを作成し、毎日または毎週、営業時間外に再作成することをお勧めします。

CREATE INDEX car_pos_time_idx ON car_pos (time DESC);
WHERE time > $mydate

$mydate は の結果です(now() - interval '1 day')。これは、いつでもクエリの条件と論理的に一致します。有効性は時間の経過とともに徐々に低下します。

余談ですが、「時間」タイプの列に名前を付けないでくださいtimestamp。ドキュメントの観点から誤解を招く可能性があります。実際には、まったくtime列名として使用しないでください。これは、すべての SQL 標準の予約語であり、PostgreSQL の型名です。

于 2012-12-01T17:25:01.167 に答える
0

1つの簡単な解決策は、それを結合に変換することです

select v.id, v.pos, v.time,  v.status,  vi.name,vi.type, 
reg_owner.fullname AS registered_owner, 
gr_ben_owner.fullname AS group_beneficial_owner, 
op.fullname AS operator, 
man.fullname AS manager
from  
  car_pos v 
  left join cars vi on v.id = vi.id
  left join company reg_owner on vi.registered_owner_code = reg_owner.owcode
  left join company gr_ben_owner on vi.group_beneficial_owner_code = gr_ben_owner.owcode
  left join company op on vi.operator_code = op.owcode
  left join company man on vi.manager_code  = man.owcode
where age(now(), v.time::time with time zone) < '1 days'::interval

ただし、テーブル Company の結合を 1 つだけ実行することで可能になるのではないかと思います... への正確な構文について 100% 確信が持てず、これによりパフォーマンスが向上するかどうか疑問があります (すべての CASE- WHEN、GROUP by など) を 4 回の結合ソリューションと比較しましたが、これも機能するはずです。(cars-car_pos は 1 対 1 の関係だと思います)

select v.id, MAX(v.pos) as pos, MAX(v.time) as vtime,  MAX(v.status) as status,  MAX(vi.name) as name,MAX(vi.type) as type, 
MAX(CASE WHEN c.owcode = vi.registered_owner_code THEN c.fullname END) AS registered_owner, 
MAX(CASE WHEN c.owcode = vi.group_beneficial_owner_code THEN c.fullname END) AS group_beneficial_owner, 
MAX(CASE WHEN c.owcode = vi.operator_code THEN op.fullname END) AS operator, 
MAX(CASE WHEN c.owcode = vi.manager_code THEN man.fullname END) AS manager
from  
  car_pos v 
  left join cars vi on v.id = vi.id
  left join company c on c.owcode IN (vi.registered_owner_code, vi.group_beneficial_owner_code, vi.operator_code, vi.manager_code)
group by v.id
having age(now(), vtime::time with time zone) < '1 days'::interval

テーブル作成DDLスクリプトといくつかの挿入を質問に入れることができれば、SQLフィドルで簡単に試すことができます...

于 2012-12-01T12:14:41.777 に答える
0
select v.id, v.pos, v.time,  v.status,  vi.name,vi.type,
c1.fullname as Registered_owner,
c2.fullname as group_beneficial_owner,          
c3.fullname AS operator,   
c4.fullname AS manager 

from car_pos v 
left outer join cars vi on v.id = vi.id
left outerjoin company c1 on vi.registered_owner_code=c1.owcode
left outerjoin company c2 on vi.group_beneficial_owner_code=c2.owcode
left outerjoin company c3 on vi.operator_code=c3.owcode
left outerjoin company c4 on vi.manager_code=c4.owcode
 where age(now(), v.time::time with time zone) < '1 days'::interval
于 2012-12-01T12:21:43.593 に答える