| 
5 | 5 | 
 
  | 
6 | 6 | from dateutil.relativedelta import relativedelta  | 
7 | 7 | 
 
  | 
8 |  | -# from faker import Faker  | 
9 |  | - | 
10 |  | -# fake = Faker()  | 
11 |  | - | 
12 |  | -# faker -r=1000 -s=',' name email ean8 vat  | 
13 | 8 | 
 
  | 
14 | 9 | currencies_ids = list(range(1, 8))  | 
15 |  | -company_ids = list(range(1, 10)) + ["NULL"]  | 
 | 10 | +company_ids = list(range(1, 10)) + ['NULL']  | 
16 | 11 | # +- 10 years of data  | 
17 | 12 | days = [datetime.date.today()]  | 
18 | 13 | for __ in range(10 * 365):  | 
 | 
25 | 20 |         line = ",".join([str(currency_id), str(date), str(company_id), str(rate)])  | 
26 | 21 |         f.write(f"{line}\n")  | 
27 | 22 | 
 
  | 
28 |  | -""" CREATE UNIQUE INDEX test_cur ON res_currency_rate (currency_id, company_id, name DESC) NULLS NOT DISTINCT """  | 
 | 23 | +# Before indexes:  | 
 | 24 | +"""  | 
 | 25 | +CREATE UNIQUE INDEX res_currency_rate_unique_name_per_day ON res_currency_rate (name, currency_id, company_id);  | 
 | 26 | +-- OR  | 
 | 27 | +CREATE UNIQUE INDEX res_currency_rate_unique_name_per_day ON res_currency_rate (name, currency_id, company_id) NULLS NOT DISTINCT;  | 
 | 28 | +"""  | 
29 | 29 | 
 
  | 
 | 30 | +# Now indexes:  | 
 | 31 | +"""  | 
 | 32 | +CREATE UNIQUE INDEX res_currency_rate_reversed_unique_name_per_day ON res_currency_rate (currency_id, company_id, name DESC) NULLS NOT DISTINCT;  | 
 | 33 | +CREATE UNIQUE INDEX res_currency_rate_unique_name_per_day ON res_currency_rate (currency_id, company_id, name) NULLS NOT DISTINCT;  | 
 | 34 | +"""  | 
30 | 35 | 
 
  | 
31 |  | -# Current solution without gettind date of rate  | 
 | 36 | +# Before queries  | 
32 | 37 | """  | 
 | 38 | +SELECT DISTINCT ON ("res_currency_rate"."currency_id")   | 
 | 39 | +    "res_currency_rate"."currency_id", "res_currency_rate"."rate"  | 
 | 40 | +FROM "res_currency_rate"  | 
 | 41 | +WHERE ("res_currency_rate"."company_id" IS NULL OR "res_currency_rate"."company_id" = 1)  | 
 | 42 | +ORDER BY  | 
 | 43 | +    "res_currency_rate"."currency_id",  | 
 | 44 | +    "res_currency_rate"."company_id",  | 
 | 45 | +    CASE WHEN "res_currency_rate"."name" <= '2025-09-02'::date THEN "res_currency_rate"."name" END DESC,  | 
 | 46 | +    CASE WHEN "res_currency_rate"."name" > '2025-09-02'::date THEN "res_currency_rate"."name" END ASC;  | 
 | 47 | +
  | 
33 | 48 | SELECT  | 
34 | 49 |     "res_currency"."id",  | 
35 | 50 |     COALESCE(  | 
 | 
66 | 81 |         ), 1.0  | 
67 | 82 |     )  | 
68 | 83 | FROM  | 
69 |  | -    "res_currency"  | 
70 |  | -WHERE "res_currency"."id" IN %s  | 
71 |  | -"""  | 
72 |  | - | 
73 |  | -""" 10 currencies  | 
74 |  | - Index Only Scan using res_currency_pkey on res_currency  (cost=0.14..24132.82 rows=7 width=36) (actual time=7.065..19.959 rows=7 loops=1)  | 
75 |  | -   Index Cond: (id = ANY ('{4,5,3,2,6,8,1}'::integer[]))  | 
76 |  | -   Heap Fetches: 7  | 
77 |  | -   SubPlan 1  | 
78 |  | -     ->  Limit  (cost=1768.76..1768.76 rows=1 width=20) (actual time=2.845..2.845 rows=1 loops=7)  | 
79 |  | -           ->  Sort  (cost=1768.76..1772.05 rows=1317 width=20) (actual time=2.844..2.844 rows=1 loops=7)  | 
80 |  | -                 Sort Key: res_currency_rate.company_id, res_currency_rate.name DESC  | 
81 |  | -                 Sort Method: quicksort  Memory: 25kB  | 
82 |  | -                 ->  Index Scan using res_currency_rate__currency_id_index on res_currency_rate  (cost=0.42..1762.17 rows=1317 width=20) (actual time=0.005..2.221 rows=6259 loops=7)  | 
83 |  | -                       Index Cond: (currency_id = res_currency.id)  | 
84 |  | -                       Filter: (((company_id = 1) OR (company_id IS NULL)) AND (name <= '2025-10-06'::date) AND ((company_id = 1) OR (company_id IS NULL)))  | 
85 |  | -                       Rows Removed by Filter: 25035  | 
86 |  | -   SubPlan 2  | 
87 |  | -     ->  Limit  (cost=1677.48..1677.48 rows=1 width=20) (actual time=0.003..0.003 rows=0 loops=1)  | 
88 |  | -           ->  Sort  (cost=1677.48..1680.77 rows=1317 width=20) (actual time=0.003..0.003 rows=0 loops=1)  | 
89 |  | -                 Sort Key: res_currency_rate_1.company_id, res_currency_rate_1.name  | 
90 |  | -                 Sort Method: quicksort  Memory: 25kB  | 
91 |  | -                 ->  Index Scan using res_currency_rate__currency_id_index on res_currency_rate res_currency_rate_1  (cost=0.42..1670.89 rows=1317 width=20) (actual time=0.001..0.001 rows=0 loops=1)  | 
92 |  | -                       Index Cond: (currency_id = res_currency.id)  | 
93 |  | -                       Filter: (((company_id = 1) OR (company_id IS NULL)) AND ((company_id = 1) OR (company_id IS NULL)))  | 
94 |  | - Planning Time: 0.418 ms  | 
95 |  | - Execution Time: 20.002 ms  | 
96 |  | -(22 rows)  | 
97 |  | -"""  | 
98 |  | - | 
99 |  | -""" 2 currencies  | 
100 |  | - Index Only Scan using res_currency_pkey on res_currency  (cost=0.14..6900.81 rows=2 width=36) (actual time=7.319..9.923 rows=2 loops=1)  | 
101 |  | -   Index Cond: (id = ANY ('{4,1}'::integer[]))  | 
102 |  | -   Heap Fetches: 2  | 
103 |  | -   SubPlan 1  | 
104 |  | -     ->  Limit  (cost=1768.76..1768.76 rows=1 width=20) (actual time=4.946..4.947 rows=1 loops=2)  | 
105 |  | -           ->  Sort  (cost=1768.76..1772.05 rows=1317 width=20) (actual time=4.945..4.945 rows=1 loops=2)  | 
106 |  | -                 Sort Key: res_currency_rate.company_id, res_currency_rate.name DESC  | 
107 |  | -                 Sort Method: top-N heapsort  Memory: 25kB  | 
108 |  | -                 ->  Index Scan using res_currency_rate__currency_id_index on res_currency_rate  (cost=0.42..1762.17 rows=1317 width=20) (actual time=0.012..3.825 rows=7302 loops=2)  | 
109 |  | -                       Index Cond: (currency_id = res_currency.id)  | 
110 |  | -                       Filter: (((company_id = 1) OR (company_id IS NULL)) AND (name <= '2025-10-06'::date) AND ((company_id = 1) OR (company_id IS NULL)))  | 
111 |  | -                       Rows Removed by Filter: 29208  | 
112 |  | -   SubPlan 2  | 
113 |  | -     ->  Limit  (cost=1677.48..1677.48 rows=1 width=20) (never executed)  | 
114 |  | -           ->  Sort  (cost=1677.48..1680.77 rows=1317 width=20) (never executed)  | 
115 |  | -                 Sort Key: res_currency_rate_1.company_id, res_currency_rate_1.name  | 
116 |  | -                 ->  Index Scan using res_currency_rate__currency_id_index on res_currency_rate res_currency_rate_1  (cost=0.42..1670.89 rows=1317 width=20) (never executed)  | 
117 |  | -                       Index Cond: (currency_id = res_currency.id)  | 
118 |  | -                       Filter: (((company_id = 1) OR (company_id IS NULL)) AND ((company_id = 1) OR (company_id IS NULL)))  | 
119 |  | - Planning Time: 0.427 ms  | 
120 |  | - Execution Time: 9.969 ms  | 
121 |  | -(21 rows)  | 
 | 84 | +    "res_currency";  | 
122 | 85 | """  | 
123 | 86 | 
 
  | 
124 |  | -# Solution use for left join  | 
 | 87 | +# New solution  | 
125 | 88 | """  | 
126 |  | -SELECT DISTINCT ON ("res_currency_rate"."currency_id")   | 
127 |  | -    "res_currency_rate"."currency_id", "res_currency_rate"."rate"  | 
128 |  | -FROM "res_currency_rate"  | 
129 |  | -WHERE ("res_currency_rate"."company_id" IS NULL OR "res_currency_rate"."company_id" = 1)   | 
130 |  | -AND "res_currency_rate"."currency_id" IN %s  | 
131 |  | -ORDER BY  | 
132 |  | -    "res_currency_rate"."currency_id",  | 
133 |  | -    "res_currency_rate"."company_id",  | 
134 |  | -    CASE WHEN "res_currency_rate"."name" <= '2025-09-02'::date THEN "res_currency_rate"."name" END DESC,  | 
135 |  | -    CASE WHEN "res_currency_rate"."name" > '2025-09-02'::date THEN "res_currency_rate"."name" END ASC  | 
136 |  | -"""  | 
137 |  | - | 
138 |  | -"""  | 
139 |  | - Unique  (cost=1697.99..12509.19 rows=7 width=28) (actual time=7.591..25.273 rows=6 loops=1)  | 
140 |  | -   ->  Incremental Sort  (cost=1697.99..12404.92 rows=41710 width=28) (actual time=7.590..24.304 rows=43812 loops=1)  | 
141 |  | -         Sort Key: currency_id, company_id, (CASE WHEN (name <= '2025-09-02'::date) THEN name ELSE NULL::date END) DESC, (CASE WHEN (name > '2025-09-02'::date) THEN name ELSE NULL::date END)  | 
142 |  | -         Presorted Key: currency_id  | 
143 |  | -         Full-sort Groups: 6  Sort Method: quicksort  Average Memory: 28kB  Peak Memory: 28kB  | 
144 |  | -         Pre-sorted Groups: 6  Sort Method: quicksort  Average Memory: 561kB  Peak Memory: 561kB  | 
145 |  | -         ->  Index Scan using res_currency_rate__currency_id_index on res_currency_rate  (cost=0.42..9268.03 rows=41710 width=28) (actual time=0.040..14.706 rows=43812 loops=1)  | 
146 |  | -               Index Cond: (currency_id = ANY ('{4,5,3,2,6,8,1}'::integer[]))  | 
147 |  | -               Filter: ((company_id IS NULL) OR (company_id = 1))  | 
148 |  | -               Rows Removed by Filter: 175248  | 
149 |  | - Planning Time: 0.218 ms  | 
150 |  | - Execution Time: 25.322 ms  | 
151 |  | -"""  | 
152 |  | - | 
153 |  | -"""  | 
154 |  | - Unique  (cost=558.49..4113.84 rows=7 width=28) (actual time=14.670..23.277 rows=2 loops=1)  | 
155 |  | -   ->  Incremental Sort  (cost=558.49..4079.37 rows=13786 width=28) (actual time=14.668..22.420 rows=14604 loops=1)  | 
156 |  | -         Sort Key: currency_id, company_id, (CASE WHEN (name <= '2025-09-02'::date) THEN name ELSE NULL::date END) DESC, (CASE WHEN (name > '2025-09-02'::date) THEN name ELSE NULL::date END)  | 
157 |  | -         Presorted Key: currency_id  | 
158 |  | -         Full-sort Groups: 2  Sort Method: quicksort  Average Memory: 28kB  Peak Memory: 28kB  | 
159 |  | -         Pre-sorted Groups: 2  Sort Method: quicksort  Average Memory: 561kB  Peak Memory: 561kB  | 
160 |  | -         ->  Index Scan using res_currency_rate__currency_id_index on res_currency_rate  (cost=0.42..3152.57 rows=13786 width=28) (actual time=0.038..13.422 rows=14604 loops=1)  | 
161 |  | -               Index Cond: (currency_id = ANY ('{4,1}'::integer[]))  | 
162 |  | -               Filter: ((company_id IS NULL) OR (company_id = 1))  | 
163 |  | -               Rows Removed by Filter: 58416  | 
164 |  | - Planning Time: 0.237 ms  | 
165 |  | - Execution Time: 23.334 ms  | 
166 |  | -"""  | 
167 |  | - | 
168 |  | -"""  | 
169 |  | -SELECT DISTINCT ON ("res_currency_rate"."currency_id")  | 
170 |  | -    "res_currency_rate"."currency_id", "res_currency_rate"."rate"  | 
171 |  | -FROM "res_currency_rate"  | 
172 |  | -WHERE ("res_currency_rate"."company_id" IS NULL OR "res_currency_rate"."company_id" = 1)  | 
173 |  | -AND "res_currency_rate"."currency_id" IN %s  | 
174 |  | -ORDER BY  | 
175 |  | -    "res_currency_rate"."currency_id",  | 
176 |  | -    "res_currency_rate"."company_id",  | 
177 |  | -    "res_currency_rate"."name" DESC;  | 
 | 89 | +EXPLAIN ANALYSE SELECT  | 
 | 90 | +    "res_currency"."id",  | 
 | 91 | +    COALESCE("before_rate"."rate", "after_rate"."rate", 1.0) AS "rate",  | 
 | 92 | +    COALESCE("before_rate"."name", "after_rate"."name") AS "name"  | 
 | 93 | +FROM  | 
 | 94 | +    "res_currency"  | 
 | 95 | +    LEFT JOIN LATERAL (  | 
 | 96 | +        SELECT  | 
 | 97 | +            "rate",  | 
 | 98 | +            "name"  | 
 | 99 | +        FROM  | 
 | 100 | +            "res_currency_rate"  | 
 | 101 | +        WHERE  | 
 | 102 | +            (  | 
 | 103 | +                (  | 
 | 104 | +                    "res_currency_rate"."company_id" IN (1)  | 
 | 105 | +                    OR "res_currency_rate"."company_id" IS NULL  | 
 | 106 | +                )  | 
 | 107 | +                AND "res_currency_rate"."name" <= '2025-10-09'  | 
 | 108 | +            )  | 
 | 109 | +            AND "res_currency_rate"."currency_id" = "res_currency"."id"  | 
 | 110 | +        ORDER BY  | 
 | 111 | +            "res_currency_rate"."company_id",  | 
 | 112 | +            "res_currency_rate"."name" DESC  | 
 | 113 | +        LIMIT  | 
 | 114 | +            1  | 
 | 115 | +    ) AS "before_rate" ON (TRUE)  | 
 | 116 | +    LEFT JOIN LATERAL (  | 
 | 117 | +        SELECT  | 
 | 118 | +            "rate",  | 
 | 119 | +            "name"  | 
 | 120 | +        FROM  | 
 | 121 | +            "res_currency_rate"  | 
 | 122 | +        WHERE  | 
 | 123 | +            (  | 
 | 124 | +                "res_currency_rate"."company_id" IN (1)  | 
 | 125 | +                OR "res_currency_rate"."company_id" IS NULL  | 
 | 126 | +            )  | 
 | 127 | +            AND "res_currency_rate"."currency_id" = "res_currency"."id"  | 
 | 128 | +        ORDER BY  | 
 | 129 | +            "res_currency_rate"."company_id",  | 
 | 130 | +            "res_currency_rate"."name" ASC  | 
 | 131 | +        LIMIT 1  | 
 | 132 | +    ) AS "after_rate" ON ("before_rate"."rate" IS NULL);  | 
178 | 133 | """  | 
179 |  | - | 
180 |  | - | 
0 commit comments