Нужно вытащить последнюю реинкарнацию организации - с максимальной org_version для данной org_id Почему postgres настолько ошибается в  оценках и как сделать чтоб не ошибался?
                  
                  
                  rdb=# \d tt
                  
                  
                                     Table "public.tt"
                  
                  
                     Column    |  Type   | Collation | Nullable | Default 
                  
                  
                  -------------+---------+-----------+----------+---------
                  
                  
                   org_id      | integer |           |          | 
                  
                  
                   org_version | integer |           |          | 
                  
                  
                  Indexes:
                  
                  
                      "tt_id_version" btree (org_id, org_version)
                  
                  
                  
                  
                  
                  rdb=# 
                  
                  
                  rdb=# explain (buffers,analyze)
                  
                  
                  select * from tt d1,
                  
                  
                  (select org.org_id,
                  
                  
                              max(org.org_version) AS max_org_version
                  
                  
                             FROM tt org
                  
                  
                            WHERE org.org_id >= 0 GROUP by org.org_id) d2
                  
                  
                  where d1.org_id = d2.org_id and  d1.org_version = d2.max_org_version;
                  
                  
                                                                         QUERY PLAN                                                        
                  
                  
                  -------------------------------------------------------------------------------------------------------------------------
                  
                  
                   Hash Join  (cost=56.28..80.23 rows=6 width=16) (actual time=0.609..0.869 rows=847 loops=1)
                  
                  
                     Hash Cond: ((d1.org_id = org.org_id) AND (d1.org_version = (max(org.org_version))))
                  
                  
                     Buffers: shared hit=12
                  
                  
                     ->  Seq Scan on tt d1  (cost=0.00..17.77 rows=1177 width=8) (actual time=0.012..0.074 rows=1177 loops=1)
                  
                  
                           Buffers: shared hit=6
                  
                  
                     ->  Hash  (cost=43.56..43.56 rows=848 width=8) (actual time=0.587..0.588 rows=847 loops=1)
                  
                  
                           Buckets: 1024  Batches: 1  Memory Usage: 42kB
                  
                  
                           Buffers: shared hit=6
                  
                  
                           ->  HashAggregate  (cost=26.60..35.08 rows=848 width=8) (actual time=0.391..0.485 rows=847 loops=1)
                  
                  
                                 Group Key: org.org_id
                  
                  
                                 Batches: 1  Memory Usage: 169kB
                  
                  
                                 Buffers: shared hit=6
                  
                  
                                 ->  Seq Scan on tt org  (cost=0.00..20.71 rows=1177 width=8) (actual time=0.007..0.128 rows=1176 loops=1)
                  
                  
                                       Filter: (org_id >= 0)
                  
                  
                                       Rows Removed by Filter: 1
                  
                  
                                       Buffers: shared hit=6
                  
                  
                   Planning:
                  
                  
                     Buffers: shared hit=3
                  
                  
                   Planning Time: 0.352 ms
                  
                  
                   Execution Time: 0.959 ms
                  
                  
                  (20 rows)
                  
                  
                  
                  
                  
                  rdb=#
                  
                  
                
И Вы нашли для этого один из наихудших способов. ;( Почему бы не попробовать что-то получше (LATERAL, если есть таблица организаций; loose index scan (rCTE) или DISTINCT ON, если нет)? И, кстати, что б ему и не ошибаться в этом случае (в таблице даже ни одного ключа нет)? ;)
Я вот эти все извращения не очень понимаю. Но даже если сделать уникальный индекс на оба поля - всё равно ошибается. Надо объявить его как Primary Key?
Не понимаете — сидите с [в среднем так бывает] производительностью на два порядка (или более) худшей, чем могла бы быть — дело Ваше. ;( > всё равно ошибается. А ANALYZE выполняли? И вообще, это план на реальных данных (иначе обсуждать это я практического смысла не вижу)?
В реальности таблица шире, число строк точное. Analyze делал
Тогда нужно копаться с конкретными данными и смотреть, как планировщик оценивает этот JOIN (но "чудо" там вряд ли будет — подобные оценки в принципе довольно грубы)...
А, собственно, почему? По-моему, как в его случае (когда org_version в среднем не сильно большэ одного на org_id) -- вменяемый довольно способ. Хотя, конечно... Планировщик за такое спасибо не скажэт.
Зачем для каждой организации свой счетчик, можно же сделать один на всех и брать его max + фильтр по org_id?
Потому что объективные измерения (benchmarks) альтернативных способов (даже для разных распределений данных) показывают, что это так — погуглите или попробуйте.
Кстати, если чаще всего нужэн максимальный org_version -- то имеет смысл начинать с создания индэкса (org_id, org_version DESC) Чуть-чуть быстрее будут типичные операцыи и чуть-чуть лояльнее к нему отнесётся планировщик.
Да на какие два порядка, не будет там 10мкс на 1000 строк хоть обпишысь...
Кстати, я правильно помню, что самый поддержываемый синтаксисом DISTINCT ON -- до сих пор не умеет index skip scan?
И, в итоге, самый быстрый результат таких запросов -- пишэтся через WITH RECURSIVE ? (Но и то -- не за счёт того, что планировщик там что-то понимает, а как раз наоборот).
Где я говорил именно про 1000 строк?! Я написал "в среднем так бывает", безотносительно к конкретному примеру (я вообще подумал, что это просто тестовые данные / демонстрация).
Так такого executor node до сих пор не существует — как он может его "уметь"? ;(
Самый быстрый (в норме) — LATERAL (или DISTINCT ON, если на каждое значение... org_id, в этом случае, приходится только 2-3 rows).
Ну, мало ли... На WITH RECURSIVE разложыть, например... Да и вообще. Есть многое на свете, друг Горацый, что мне лично и не снилось.
Не, все ждут, пока https://wiki.postgresql.org/wiki/Loose_indexscan кто-то "честно" реализует, зачем нам полумеры? ;)
Обсуждают сегодня