使用索引扫描向后改进SQL语句

时间:2015-05-04 12:04:11

标签: python performance postgresql odoo

我们正在使用python软件Odoo,它使用ORM进行数据库请求(PostgreSQL)。目前我正在尝试减少某些SQL语句的执行时间。我可以通过添加一些索引来增加时间。

现在我有一个查询需要花费很多时间而且不能减少执行时间。你能帮助我提高这个查询的速度吗?我无法更改查询本身(可能只是一些小部分),但也许我们可以更改表结构本身的某些内容?

查询:

SELECT "purchase_order".id
FROM "mail_message" AS "purchase_order__message_ids",
     "mail_notification" AS "purchase_order__message_ids__notification_ids",
     "purchase_order"
WHERE ("purchase_order"."id"="purchase_order__message_ids"."res_id"
       AND "purchase_order__message_ids"."id"="purchase_order__message_ids__notification_ids"."message_id")
  AND ((("purchase_order"."state" NOT IN ('draft',
                                          'sent',
                                          'bid',
                                          'confirmed'))
        OR "purchase_order"."state" IS NULL)
       AND (("purchase_order__message_ids"."model" = 'purchase.order')
            AND (("purchase_order__message_ids__notification_ids"."partner_id" IN (3))
                 AND ("purchase_order__message_ids__notification_ids"."is_read" IS NULL
                      OR "purchase_order__message_ids__notification_ids"."is_read" = FALSE))))
ORDER BY "purchase_order"."id" DESC LIMIT 100;

以下是EXPLAIN ANALYZE的输出:

# EXPLAIN ANALYZE SELECT "purchase_order".id FROM "mail_message" as "purchase_order__message_ids","mail_notification" as "purchase_order__message_ids__notification_ids","purchase_order" WHERE ("purchase_order"."id"="purchase_order__message_ids"."res_id" AND "purchase_order__message_ids"."id"="purchase_order__message_ids__notification_ids"."message_id") AND ((("purchase_order"."state" not in ('draft','sent','bid','confirmed')) OR "purchase_order"."state" IS NULL)  AND  (("purchase_order__message_ids"."model" = 'purchase.order')  AND  (("purchase_order__message_ids__notification_ids"."partner_id" in (3))  AND  ("purchase_order__message_ids__notification_ids"."is_read" IS NULL or "purchase_order__message_ids__notification_ids"."is_read" = false )))) ORDER BY "purchase_order"."id" DESC  limit 100;
                                                                                                        QUERY PLAN                                                                                                        
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.99..10720.11 rows=100 width=4) (actual time=956.615..958.683 rows=2 loops=1)
   ->  Nested Loop  (cost=0.99..63779.73 rows=595 width=4) (actual time=956.609..958.669 rows=2 loops=1)
         ->  Nested Loop  (cost=0.71..63177.50 rows=1325 width=4) (actual time=956.573..958.609 rows=2 loops=1)
               ->  Index Scan Backward using mail_message_res_id_index on mail_message purchase_order__message_ids  (cost=0.42..48385.44 rows=40025 width=8) (actual time=110.607..518.171 rows=40149 loops=1)
                     Filter: ((model)::text = 'purchase.order'::text)
                     Rows Removed by Filter: 254269
               ->  Index Scan using mail_notification_message_id_index on mail_notification purchase_order__message_ids__notification_ids  (cost=0.29..0.36 rows=1 width=4) (actual time=0.006..0.006 rows=0 loops=40149)
                     Index Cond: (message_id = purchase_order__message_ids.id)
                     Filter: (((is_read IS NULL) OR (NOT is_read)) AND (partner_id = 3))
                     Rows Removed by Filter: 0
         ->  Index Scan using purchase_order_pkey on purchase_order  (cost=0.28..0.44 rows=1 width=4) (actual time=0.017..0.019 rows=1 loops=2)
               Index Cond: (id = purchase_order__message_ids.res_id)
               Filter: (((state)::text <> ALL ('{draft,sent,bid,confirmed}'::text[])) OR (state IS NULL))
 Planning time: 2.468 ms
 Execution time: 958.792 ms
(15 rows)

您可以在http://explain.depesz.com/s/cbG

上观看说明

仅供注意 - 行数:

          table           | num_rows 
--------------------------+----------
 public.mail_notification |    42254
 public.mail_message      |   294474
 public.purchase_order    |     6566
(3 rows)

以下是\d mail_message

的输出
# \d mail_message
                                       Table "public.mail_message"
     Column     |            Type             |                         Modifiers                         
----------------+-----------------------------+-----------------------------------------------------------
 id             | integer                     | not null default nextval('mail_message_id_seq'::regclass)
 create_uid     | integer                     | 
 create_date    | timestamp without time zone | 
 write_date     | timestamp without time zone | 
 write_uid      | integer                     | 
 body           | text                        | 
 model          | character varying(128)      | 
 record_name    | character varying           | 
 date           | timestamp without time zone | 
 subject        | character varying           | 
 message_id     | character varying           | 
 parent_id      | integer                     | 
 res_id         | integer                     | 
 subtype_id     | integer                     | 
 author_id      | integer                     | 
 type           | character varying           | 
 email_from     | character varying           | 
 mail_server_id | integer                     | 
 no_auto_thread | boolean                     | 
 reply_to       | character varying           | 
Indexes:
    "mail_message_pkey" PRIMARY KEY, btree (id)
    "mail_message_author_id_index" btree (author_id)
    "mail_message_message_id_index" btree (message_id)
    "mail_message_model_index" btree (model)
    "mail_message_model_res_id_idx" btree (model, res_id)
    "mail_message_parent_id_index" btree (parent_id)
    "mail_message_res_id_index" btree (res_id)
    "mail_message_subtype_id_index" btree (subtype_id)
Foreign-key constraints:
    "mail_message_author_id_fkey" FOREIGN KEY (author_id) REFERENCES res_partner(id) ON DELETE SET NULL
    "mail_message_create_uid_fkey" FOREIGN KEY (create_uid) REFERENCES res_users(id) ON DELETE SET NULL
    "mail_message_mail_server_id_fkey" FOREIGN KEY (mail_server_id) REFERENCES ir_mail_server(id) ON DELETE SET NULL
    "mail_message_parent_id_fkey" FOREIGN KEY (parent_id) REFERENCES mail_message(id) ON DELETE SET NULL
    "mail_message_subtype_id_fkey" FOREIGN KEY (subtype_id) REFERENCES mail_message_subtype(id) ON DELETE SET NULL
    "mail_message_write_uid_fkey" FOREIGN KEY (write_uid) REFERENCES res_users(id) ON DELETE SET NULL
Referenced by:
    TABLE "crm_lead_forward_to_partner" CONSTRAINT "crm_lead_forward_to_partner_parent_id_fkey" FOREIGN KEY (parent_id) REFERENCES mail_message(id) ON DELETE SET NULL
    TABLE "mail_compose_message" CONSTRAINT "mail_compose_message_parent_id_fkey" FOREIGN KEY (parent_id) REFERENCES mail_message(id) ON DELETE SET NULL
    TABLE "mail_mail" CONSTRAINT "mail_mail_mail_message_id_fkey" FOREIGN KEY (mail_message_id) REFERENCES mail_message(id) ON DELETE CASCADE
    TABLE "mail_message" CONSTRAINT "mail_message_parent_id_fkey" FOREIGN KEY (parent_id) REFERENCES mail_message(id) ON DELETE SET NULL
    TABLE "mail_message_res_partner_rel" CONSTRAINT "mail_message_res_partner_rel_mail_message_id_fkey" FOREIGN KEY (mail_message_id) REFERENCES mail_message(id) ON DELETE CASCADE
    TABLE "mail_notification_bcc" CONSTRAINT "mail_notification_bcc_message_id_fkey" FOREIGN KEY (message_id) REFERENCES mail_message(id) ON DELETE CASCADE
    TABLE "mail_notification_cc" CONSTRAINT "mail_notification_cc_message_id_fkey" FOREIGN KEY (message_id) REFERENCES mail_message(id) ON DELETE CASCADE
    TABLE "mail_notification" CONSTRAINT "mail_notification_message_id_fkey" FOREIGN KEY (message_id) REFERENCES mail_message(id) ON DELETE CASCADE
    TABLE "mail_vote" CONSTRAINT "mail_vote_message_id_fkey" FOREIGN KEY (message_id) REFERENCES mail_message(id) ON DELETE CASCADE
    TABLE "message_attachment_rel" CONSTRAINT "message_attachment_rel_message_id_fkey" FOREIGN KEY (message_id) REFERENCES mail_message(id) ON DELETE CASCADE

1 个答案:

答案 0 :(得分:1)

您可以为特定查询创建选择性索引,例如

CREATE INDEX idx_order_messages on mail_message (id) where model = 'purchase.order'

较小的索引导致较少的读取等,因此它必须更快。