Skip to content

Instantly share code, notes, and snippets.

@jmcarp
Last active July 13, 2023 19:24
Show Gist options
  • Select an option

  • Save jmcarp/384310cb3925eaa3b3ca to your computer and use it in GitHub Desktop.

Select an option

Save jmcarp/384310cb3925eaa3b3ca to your computer and use it in GitHub Desktop.
Approximate query count with PostgreSQL and SQLAlchemy
"""Approximate query count based on ANALYZE output for PostgreSQL and SQLAlchemy.
Count logic borrowed from https://wiki.postgresql.org/wiki/Count_estimate
ANALYZE borrowed from https://bitbucket.org/zzzeek/sqlalchemy/wiki/UsageRecipes/Explain
"""
import re
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.expression import Executable, ClauseElement, _literal_as_text
count_pattern = re.compile(r'rows=(\d+)')
def extract_analyze_count(rows):
for row in rows:
match = count_pattern.search(row[0])
if match:
return int(match.groups()[0])
def count_estimate(query, session, threshold=None):
rows = session.execute(explain(query)).fetchall()
count = extract_analyze_count(rows)
if threshold is not None and count < threshold:
return query.count()
return count
class explain(Executable, ClauseElement):
def __init__(self, stmt, analyze=False):
self.statement = _literal_as_text(stmt)
self.analyze = analyze
# helps with INSERT statements
self.inline = getattr(stmt, 'inline', None)
@compiles(explain, 'postgresql')
def pg_explain(element, compiler, **kw):
text = 'EXPLAIN '
if element.analyze:
text += 'ANALYZE '
text += compiler.process(element.statement, **kw)
return text
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment