I wonder how do Big Co solve this counting problem.
In the same way, asking for counts may or may not be a quick answer, depending on both the table structure and the query being performed. Query the number of customers who started this year, on a table indexed by start date, and it's incredibly cheap. If that same table is only indexed by last name, then counts become more expensive. I'd say that the advice to return the counts has an implicit addendum to make sure that your database is appropriately indexed such that your typical queries are cheap to perform.
Meanwhile I can only think of one use case for pagination with exact row counts – a user facing application. If you're preallocating storage you can get by with an estimate. If you're consuming an API and want something from near the end you can flip the sort order.
There is no single universal row count that the database could cache, so it must
scan through all rows counting how many are visible. Performance for an exact
count grows linearly with table size.
https://www.citusdata.com/blog/2016/10/12/count-performance/ But now we come to a quirk, SELECT COUNT(DISTINCT n) FROM items will not use
the index even though SELECT DISTINCT n does. As many blog posts mention (“one
weird trick to make postgres 50x faster!”) you can guide the planner by rewriting
count distinct as the count of a subquery
To me it seems like you can trick the query planner into doing an index scan sometimes, but that it's a bit brittle. Has this improved much since?https://wiki.postgresql.org/wiki/Index-only_scans#Is_.22coun...
It says that index only scans can be used with predicates and less often without predicates, though in my experience I've seen the index often used even without predicates.
Index-only scans are opportunistic, in that they take advantage of a pre-existing
state of affairs where it happens to be possible to elide heap access. However,
the server doesn't make any particular effort to facilitate index-only scans,
and it is difficult to recommend a course of action to make index-only scans
occur more frequently, except to define covering indexes in response to a
measured need
And: Index-only scans are only used when the planner surmises that that will reduce the
total amount of I/O required, according to its imperfect cost-based modelling. This all
heavily depends on visibility of tuples, if an index would be used anyway (i.e. how
selective a predicate is, etc), and if there is actually an index available that could
be used by an index-only scan in principle.
So yeah, I don't know if your use case is exceptionally lucky or if the documentation is just pessimistic (or both). Good to know you can coerce the query planner into doing an index scan though.The "shortcuts" you're thinking of may be from the MyISAM storage engine days. Since MyISAM doesn't have transactions or MVCC, it can maintain a row count for the table and just return that. But MyISAM is generally not used for the past decade (or more), as it just isn't really suitable for storing data that you care about.
Storage engines with transactions cannot just store a row count per table because the accurate count always depends on your transaction's isolated snapshot.
Do you really need to paginate millions of rows and count them? Can you distribute the database and parallelize counting? Can you save counts somewhere else? Can you use estimates?