This is a real design bug.

To analyse any query, please run:
EXPLAIN ANALYSE yourSQL query

i.e.
EXPLAIN ANALYSE 
SELECT min(account_invoice_report.id) AS id, count(account_invoice_report.id) 
AS categ_id_count,"account_invoice_report".categ_id, 
sum("account_invoice_report"."nbr") AS nbr, 
sum("account_invoice_report"."product_qty") AS product_qty, 
sum("account_invoice_report"."price_total") AS price_total FROM 
"account_invoice_report" WHERE ((((account_invoice_report."date" <= 
'2012-05-18') AND (account_invoice_report."date" >= '2012-01-01')) AND 
((account_invoice_report."state" not in ('draft','cancel')) OR 
account_invoice_report."state" IS NULL)) AND ((account_invoice_report."type" = 
'out_invoice') OR (account_invoice_report."type" = 'out_refund'))) GROUP BY 
"account_invoice_report".categ_id

On my server, even with on 3000 clients ...
the query returns a result in 20 seconds.

"HashAggregate  (cost=267661920.65..267661925.15 rows=200 width=32) (actual 
time=20619.984..20619.986 rows=2 loops=1)"
"  ->  GroupAggregate  (cost=2363456.42..267585641.15 rows=3051180 width=184) 
(actual time=20459.610..20617.810 rows=1257 loops=1)"
"        ->  Sort  (cost=2363456.42..2371084.37 rows=3051180 width=184) (actual 
time=20459.384..20459.784 rows=1265 loops=1)"
"              Sort Key: ail.product_id, ai.date_invoice, ai.id, cr.rate, 
(to_char((ai.date_invoice)::timestamp with time zone, 'YYYY'::text)), 
(to_char((ai.date_invoice)::timestamp with time zone, 'MM'::text)), 
(to_char((ai.date_invoice)::timestamp with time zone, 'YYYY-MM-DD'::text)), 
ai.partner_id, ai.payment_term, ai.period_id, u.name, ai.currency_id, 
ai.journal_id, ai.fiscal_position, ai.user_id, ai.company_id, ai.type, 
ai.state, pt.categ_id, ai.date_due, ai.address_contact_id, 
ai.address_invoice_id, ai.account_id, ai.partner_bank_id, ai.residual, 
ai.amount_total, u.uom_type, u.category_id"
"              Sort Method:  quicksort  Memory: 701kB"
"              ->  Merge Join  (cost=0.00..1492526.05 rows=3051180 width=184) 
(actual time=11.478..20452.364 rows=1265 loops=1)"
"                    Merge Cond: (ail.invoice_id = ai.id)"
"                    ->  Nested Loop Left Join  (cost=0.00..1475.72 rows=3084 
width=86) (actual time=0.067..25.090 rows=2646 loops=1)"
"                          Join Filter: (u.id = ail.uos_id)"
"                          ->  Nested Loop Left Join  (cost=0.00..1058.26 
rows=3084 width=68) (actual time=0.047..15.025 rows=2646 loops=1)"
"                                ->  Index Scan using 
account_invoice_line_invoice_id_index on account_invoice_line ail  
(cost=0.00..170.50 rows=3084 width=64) (actual time=0.034..3.749 rows=2646 
loops=1)"
"                                ->  Index Scan using product_template_pkey on 
product_template pt  (cost=0.00..0.28 rows=1 width=8) (actual time=0.002..0.003 
rows=1 loops=2646)"
"                                      Index Cond: (pt.id = ail.product_id)"
"                          ->  Materialize  (cost=0.00..1.14 rows=9 width=26) 
(actual time=0.000..0.001 rows=9 loops=2646)"
"                                ->  Seq Scan on product_uom u  
(cost=0.00..1.09 rows=9 width=26) (actual time=0.004..0.010 rows=9 loops=1)"
"                    ->  Materialize  (cost=0.00..1416063.69 rows=1565388 
width=102) (actual time=1.818..20416.193 rows=1264 loops=1)"
"                          ->  Nested Loop  (cost=0.00..1412150.22 rows=1565388 
width=102) (actual time=1.812..20415.160 rows=445 loops=1)"
"                                Join Filter: (SubPlan 6)"
"                                ->  Index Scan using account_invoice_pkey on 
account_invoice ai  (cost=0.00..145.14 rows=354 width=92) (actual 
time=0.867..4.586 rows=445 loops=1)"
"                                      Filter: ((date_invoice <= 
'2012-05-18'::date) AND (date_invoice >= '2012-01-01'::date) AND 
(((state)::text <> ALL ('{draft,cancel}'::text[])) OR (state IS NULL)) AND 
(((type)::text = 'out_invoice'::text) OR ((type)::text = 'out_refund'::text)))"
"                                ->  Materialize  (cost=0.00..206.66 rows=8844 
width=18) (actual time=0.000..0.798 rows=8763 loops=445)"
"                                      ->  Seq Scan on res_currency_rate cr  
(cost=0.00..162.44 rows=8844 width=18) (actual time=0.009..2.100 rows=8763 
loops=1)"
"                                SubPlan 6"
"                                  ->  Limit  (cost=0.01..0.86 rows=1 width=4) 
(actual time=0.005..0.005 rows=1 loops=3899535)"
"                                        ->  Result  (cost=0.01..184.56 
rows=216 width=4) (actual time=0.004..0.004 rows=1 loops=3899535)"
"                                              One-Time Filter: ((($3 IS NOT 
NULL) AND ($4 <= $3)) OR (($3 IS NULL) AND ($4 <= now())))"
"                                              ->  Seq Scan on 
res_currency_rate cr2  (cost=0.01..184.56 rows=216 width=4) (actual 
time=0.007..0.007 rows=1 loops=2354355)"
"                                                    Filter: (currency_id = $2)"
"        SubPlan 1"
"          ->  Seq Scan on product_uom  (cost=0.00..1.14 rows=1 width=3) (never 
executed)"
"                Filter: (((uom_type)::text = 'reference'::text) AND 
(category_id = $0))"
"        SubPlan 2"
"          ->  Aggregate  (cost=16.62..16.63 rows=1 width=4) (actual 
time=0.009..0.009 rows=1 loops=1257)"
"                ->  Nested Loop  (cost=0.00..16.61 rows=2 width=4) (actual 
time=0.006..0.007 rows=4 loops=1257)"
"                      ->  Index Scan using account_invoice_pkey on 
account_invoice a  (cost=0.00..8.27 rows=1 width=4) (actual time=0.003..0.003 
rows=1 loops=1257)"
"                            Index Cond: (id = $1)"
"                      ->  Index Scan using 
account_invoice_line_invoice_id_index on account_invoice_line l  
(cost=0.00..8.32 rows=2 width=8) (actual time=0.002..0.002 rows=4 loops=1257)"
"                            Index Cond: (l.invoice_id = $1)"
"        SubPlan 3"
"          ->  Aggregate  (cost=26.11..26.14 rows=1 width=12) (actual 
time=0.054..0.054 rows=1 loops=1265)"
"                ->  Nested Loop Left Join  (cost=0.00..26.10 rows=6 width=12) 
(actual time=0.008..0.022 rows=22 loops=1265)"
"                      Join Filter: (a.id = l.invoice_id)"
"                      ->  Nested Loop  (cost=0.00..17.68 rows=3 width=8) 
(actual time=0.005..0.009 rows=5 loops=1265)"
"                            ->  Index Scan using account_invoice_pkey on 
account_invoice a  (cost=0.00..8.27 rows=1 width=8) (actual time=0.002..0.002 
rows=1 loops=1265)"
"                                  Index Cond: (id = $1)"
"                            ->  Index Scan using 
account_move_line_move_id_index on account_move_line aml  (cost=0.00..9.38 
rows=3 width=8) (actual time=0.002..0.003 rows=5 loops=1265)"
"                                  Index Cond: (aml.move_id = a.move_id)"
"                      ->  Materialize  (cost=0.00..8.33 rows=2 width=12) 
(actual time=0.001..0.001 rows=4 loops=6590)"
"                            ->  Index Scan using 
account_invoice_line_invoice_id_index on account_invoice_line l  
(cost=0.00..8.32 rows=2 width=12) (actual time=0.002..0.003 rows=4 loops=1260)"
"                                  Index Cond: (invoice_id = $1)"
"        SubPlan 4"
"          ->  Aggregate  (cost=26.11..26.14 rows=1 width=8) (actual 
time=0.037..0.037 rows=1 loops=1265)"
"                ->  Nested Loop Left Join  (cost=0.00..26.10 rows=6 width=8) 
(actual time=0.009..0.021 rows=22 loops=1265)"
"                      Join Filter: (a.id = l.invoice_id)"
"                      ->  Nested Loop  (cost=0.00..17.68 rows=3 width=12) 
(actual time=0.005..0.008 rows=5 loops=1265)"
"                            ->  Index Scan using account_invoice_pkey on 
account_invoice a  (cost=0.00..8.27 rows=1 width=16) (actual time=0.002..0.002 
rows=1 loops=1265)"
"                                  Index Cond: (id = $1)"
"                            ->  Index Scan using 
account_move_line_move_id_index on account_move_line aml  (cost=0.00..9.38 
rows=3 width=4) (actual time=0.002..0.004 rows=5 loops=1265)"
"                                  Index Cond: (aml.move_id = a.move_id)"
"                      ->  Materialize  (cost=0.00..8.33 rows=2 width=4) 
(actual time=0.001..0.001 rows=4 loops=6590)"
"                            ->  Index Scan using 
account_invoice_line_invoice_id_index on account_invoice_line l  
(cost=0.00..8.32 rows=2 width=4) (actual time=0.002..0.004 rows=4 loops=1260)"
"                                  Index Cond: (invoice_id = $1)"
"        SubPlan 5"
"          ->  Aggregate  (cost=16.62..16.63 rows=1 width=4) (actual 
time=0.007..0.007 rows=1 loops=1257)"
"                ->  Nested Loop  (cost=0.00..16.61 rows=2 width=4) (actual 
time=0.004..0.006 rows=4 loops=1257)"
"                      ->  Index Scan using account_invoice_pkey on 
account_invoice a  (cost=0.00..8.27 rows=1 width=4) (actual time=0.002..0.002 
rows=1 loops=1257)"
"                            Index Cond: (id = $1)"
"                      ->  Index Scan using 
account_invoice_line_invoice_id_index on account_invoice_line l  
(cost=0.00..8.32 rows=2 width=8) (actual time=0.001..0.002 rows=4 loops=1257)"
"                            Index Cond: (l.invoice_id = $1)"
"Total runtime: 20621.529 ms"

Let's try to understand what is going on.

account_invoice_report is a view, which definition is:

-- View: account_invoice_report

CREATE OR REPLACE VIEW account_invoice_report AS 
 SELECT min(ail.id) AS id, ai.date_invoice AS date, 
to_char(ai.date_invoice::timestamp with time zone, 'YYYY'::text) AS year, 
to_char(ai.date_invoice::timestamp with time zone, 'MM'::text) AS month, 
to_char(ai.date_invoice::timestamp with time zone, 'YYYY-MM-DD'::text) AS day, 
ail.product_id, ai.partner_id, ai.payment_term, ai.period_id, 
        CASE
            WHEN u.uom_type::text <> 'reference'::text THEN ( SELECT 
product_uom.name
               FROM product_uom
              WHERE product_uom.uom_type::text = 'reference'::text AND 
product_uom.category_id = u.category_id)
            ELSE u.name
        END AS uom_name, ai.currency_id, ai.journal_id, ai.fiscal_position, 
ai.user_id, ai.company_id, count(ail.*) AS nbr, ai.type, ai.state, pt.categ_id, 
ai.date_due, ai.address_contact_id, ai.address_invoice_id, ai.account_id, 
ai.partner_bank_id, sum(
        CASE
            WHEN ai.type::text = ANY (ARRAY['out_refund'::character varying, 
'in_invoice'::character varying]::text[]) THEN ail.quantity / u.factor::double 
precision * (-1)::double precision
            ELSE ail.quantity / u.factor::double precision
        END) AS product_qty, sum(
        CASE
            WHEN ai.type::text = ANY (ARRAY['out_refund'::character varying, 
'in_invoice'::character varying]::text[]) THEN ail.quantity * 
ail.price_unit::double precision * (-1)::double precision
            ELSE ail.quantity * ail.price_unit::double precision
        END) / cr.rate::double precision AS price_total, sum(
        CASE
            WHEN ai.type::text = ANY (ARRAY['out_refund'::character varying, 
'in_invoice'::character varying]::text[]) THEN ai.amount_total * (-1)::numeric
            ELSE ai.amount_total
        END) / (( SELECT count(l.id) AS count
           FROM account_invoice_line l
      LEFT JOIN account_invoice a ON a.id = l.invoice_id
     WHERE a.id = ai.id))::numeric / cr.rate AS price_total_tax, 
        CASE
            WHEN ai.type::text = ANY (ARRAY['out_refund'::character varying, 
'in_invoice'::character varying]::text[]) THEN sum(ail.quantity * 
ail.price_unit::double precision * (-1)::double precision)
            ELSE sum(ail.quantity * ail.price_unit::double precision)
        END / 
        CASE
            WHEN ai.type::text = ANY (ARRAY['out_refund'::character varying, 
'in_invoice'::character varying]::text[]) THEN sum(ail.quantity / 
u.factor::double precision * (-1)::double precision)
            ELSE sum(ail.quantity / u.factor::double precision)
        END / cr.rate::double precision AS price_average, cr.rate AS 
currency_rate, sum(( SELECT date_part('epoch'::text, 
avg(date_trunc('day'::text, aml.date_created::timestamp with time zone) - 
date_trunc('day'::text, l.create_date)::timestamp with time zone)) / (24 * 60 * 
60)::numeric(16,2)::double precision
           FROM account_move_line aml
      LEFT JOIN account_invoice a ON a.move_id = aml.move_id
   LEFT JOIN account_invoice_line l ON a.id = l.invoice_id
  WHERE a.id = ai.id)) AS delay_to_pay, sum(( SELECT date_part('epoch'::text, 
avg(date_trunc('day'::text, a.date_due::timestamp with time zone) - 
date_trunc('day'::text, a.date_invoice::timestamp with time zone))) / (24 * 60 
* 60)::numeric(16,2)::double precision
           FROM account_move_line aml
      LEFT JOIN account_invoice a ON a.move_id = aml.move_id
   LEFT JOIN account_invoice_line l ON a.id = l.invoice_id
  WHERE a.id = ai.id)) AS due_delay, 
        CASE
            WHEN ai.type::text = ANY (ARRAY['out_refund'::character varying, 
'in_invoice'::character varying]::text[]) THEN ai.residual * (-1)::numeric
            ELSE ai.residual
        END / (( SELECT count(l.id) AS count
           FROM account_invoice_line l
      LEFT JOIN account_invoice a ON a.id = l.invoice_id
     WHERE a.id = ai.id))::numeric / cr.rate AS residual
   FROM account_invoice_line ail
   LEFT JOIN account_invoice ai ON ai.id = ail.invoice_id
   LEFT JOIN product_template pt ON pt.id = ail.product_id
   LEFT JOIN product_uom u ON u.id = ail.uos_id, res_currency_rate cr
  WHERE (cr.id IN ( SELECT cr2.id
   FROM res_currency_rate cr2
  WHERE cr2.currency_id = ai.currency_id AND (ai.date_invoice IS NOT NULL AND 
cr.name <= ai.date_invoice OR ai.date_invoice IS NULL AND cr.name <= now())
 LIMIT 1))
  GROUP BY ail.product_id, ai.date_invoice, ai.id, cr.rate, 
to_char(ai.date_invoice::timestamp with time zone, 'YYYY'::text), 
to_char(ai.date_invoice::timestamp with time zone, 'MM'::text), 
to_char(ai.date_invoice::timestamp with time zone, 'YYYY-MM-DD'::text), 
ai.partner_id, ai.payment_term, ai.period_id, u.name, ai.currency_id, 
ai.journal_id, ai.fiscal_position, ai.user_id, ai.company_id, ai.type, 
ai.state, pt.categ_id, ai.date_due, ai.address_contact_id, 
ai.address_invoice_id, ai.account_id, ai.partner_bank_id, ai.residual, 
ai.amount_total, u.uom_type, u.category_id;

ALTER TABLE account_invoice_report
  OWNER TO openerp;


First, there are design problems in the view.

1) In a view, you never use conversion to cast a type.
to_char(ai.date_invoice::timestamp with time zone, 'YYYY'::text) AS year
In PostgreSQL, casting is done automatically in queries, not views.

2) use of case ...
will make the database run sequential scans.

This should be replaced either with a UNION/INTERSEC or temporary table.
There is no possibility to make this query run better.

This is really an incledible query, as it tries to achieve something
relatively easy but breaks nearly ALL SQL optimization rules.

-- 
You received this bug notification because you are a member of OpenERP
Accounting Experts, which is subscribed to a duplicate bug report
(1021321).
https://bugs.launchpad.net/bugs/1000195

Title:
  Performance issue in invoice analysis

Status in OpenERP Addons (modules):
  Confirmed

Bug description:
  The sale-module produces a lot of selects to the
  account_invoice_report database view. I think its one of the widget in
  the dashboard that does this. Anyway the performance are lousy and a
  ticking bomb for many 6.1 installations.

  
  This select takes 8000 ms with 450 invoices in the database
  SELECT month,price_total FROM "account_invoice_report" WHERE 
account_invoice_report.id IN (450 ids)

  The company creats over 200 000 invoices per year, which was OK with
  series 5 of OpenERP. It will not be possible now after upgrade to 6.1.
  There are many installations who creates more than 400 invoices per
  year, many that creats 400 invoinces per day.

  I think we need a temporary table or something instead of this
  database view. Every time a salesman opens his saleview he hits this
  problem.

To manage notifications about this bug go to:
https://bugs.launchpad.net/openobject-addons/+bug/1000195/+subscriptions

_______________________________________________
Mailing list: https://launchpad.net/~openerp-expert-accounting
Post to     : [email protected]
Unsubscribe : https://launchpad.net/~openerp-expert-accounting
More help   : https://help.launchpad.net/ListHelp

Reply via email to