2

次のテーブルがあります。

users (id, network_id)
networks (id)
private_messages (id, sender_id, receiver_id, created_at)

users.network_id とプライベート メッセージの 3 つの列すべてにインデックスがありますが、クエリはインデックスをスキップし、実行に非常に長い時間がかかります。インデックスがスキップされる原因となっているクエリの何が問題なのですか?

EXPLAIN ANALYZE SELECT COUNT(*) 
FROM "networks" 
WHERE (
          networks.created_at BETWEEN ((timestamp '2013-01-01')) AND (( (timestamp '2013-01-31') + interval '-1 second'))
          AND (SELECT COUNT(*) FROM private_messages INNER JOIN users ON private_messages.receiver_id = users.id WHERE users.network_id = networks.id AND (private_messages.created_at BETWEEN ((timestamp '2013-03-01')) AND (( (timestamp '2013-03-31') + interval '-1 second'))) ) > 0)

結果:

Aggregate  (cost=722675247.10..722675247.11 rows=1 width=0) (actual time=519916.108..519916.108 rows=1 loops=1)
  ->  Seq Scan on networks  (cost=0.00..722675245.34 rows=703 width=0) (actual time=2576.205..519916.044 rows=78 loops=1)
        Filter: ((created_at >= '2013-01-01 00:00:00'::timestamp without time zone) AND (created_at <= '2013-01-30 23:59:59'::timestamp without time zone) AND ((SubPlan 1) > 0))
        SubPlan 1
          ->  Aggregate  (cost=50671.34..50671.35 rows=1 width=0) (actual time=240.359..240.359 rows=1 loops=2163)
                ->  Hash Join  (cost=10333.69..50671.27 rows=28 width=0) (actual time=233.997..240.340 rows=13 loops=2163)
                      Hash Cond: (private_messages.receiver_id = users.id)
                      ->  Bitmap Heap Scan on private_messages  (cost=10127.11..48675.15 rows=477136 width=4) (actual time=56.599..232.855 rows=473686 loops=1809)
                            Recheck Cond: ((created_at >= '2013-03-01 00:00:00'::timestamp without time zone) AND (created_at <= '2013-03-30 23:59:59'::timestamp without time zone))
                            ->  Bitmap Index Scan on index_private_messages_on_created_at  (cost=0.00..10007.83 rows=477136 width=0) (actual time=54.551..54.551 rows=473686 loops=1809)
                                  Index Cond: ((created_at >= '2013-03-01 00:00:00'::timestamp without time zone) AND (created_at <= '2013-03-30 23:59:59'::timestamp without time zone))
                      ->  Hash  (cost=205.87..205.87 rows=57 width=4) (actual time=0.218..0.218 rows=2 loops=2163)
                            Buckets: 1024  Batches: 1  Memory Usage: 0kB
                            ->  Index Scan using index_users_on_network_id on users  (cost=0.00..205.87 rows=57 width=4) (actual time=0.154..0.215 rows=2 loops=2163)
                                  Index Cond: (network_id = networks.id)
Total runtime: 519916.183 ms

ありがとうございました。

4

2 に答える 2

1

まず、 にインデックスが必要だと思いますが、network.created_at現在はテーブルの 10% 以上が に一致しているためWHERE、おそらく使用されないでしょう。

次に、一部のロジックをサブクエリに分割するのではなく、できるだけ多くのロジックを 1 つのクエリに取り込もうとすると、速度が向上することが期待されます。計画はnetwork.id、一致する各値を反復処理することを示していると思います。通常は、一度に参加する方がうまく機能します。

以下のコードは論理的に同等だと思います。そうでない場合は、閉じます。

SELECT COUNT(*) 
FROM 
 (SELECT users.network_id FROM "networks"
  JOIN users 
  ON users.network_id = networks.id
  JOIN private_messages
  ON private_messages.receiver_id = users.id
   AND (private_messages.created_at 
    BETWEEN ((timestamp '2013-03-01')) 
         AND (( (timestamp '2013-03-31') + interval '-1 second')))
  WHERE 
   networks.created_at 
    BETWEEN ((timestamp '2013-01-01')) 
     AND (( (timestamp '2013-01-31') + interval '-1 second'))
  GROUP BY users.network_id)
     AS main_subquery  
;

私の経験では、結合networks.created_atON句に移動すると、同じクエリ プランが得られます。users-networksあなたの問題はタイムスタンプではないと思います。それはクエリの構造です。GROUP BYサブクエリの を に置き換えることで、より良い (またはより悪い) 計画を取得することもできますSELECT DISTINCT users.network_id

于 2013-04-12T20:10:43.693 に答える