Haki Benita
hakibenita.com · Personal blog · on HN since 2019
Haki Benita writes detailed articles on SQL, PostgreSQL performance, Django, and Python, with practical examples and benchmarks.
What HN saysCommenters pick up on specifics such as the MERGE tip, cite his SQL tricks for application DBAs, and list him among good personal blogs.
52HN people shared it
15linked it in comments
67stories submitted
3.6Kstory points
4 months agolast shared
2019people per year2026
Most-shared posts
- 1Practical SQL for Data Analysis6 people · 615 points · first shared 2021 · 191 comments on HN
- 2Simple Anomaly Detection Using Plain SQL6 people · 528 points · first shared 2020 · 47 comments on HN
- 3Improve Serialization Performance in Django Rest Framework Including Banchmarks5 people · 4 points · first shared 2019 · discussion on HN
- 4Lesser-known Postgres features4 people · 709 points · first shared 2021 · 166 comments on HN
- 5Some SQL Tricks of an Application DBA4 people · 336 points · first shared 2020 · 68 comments on HN
- 6The surprising impact of medium-size texts on PostgreSQL performance4 people · 189 points · first shared 2020 · 16 comments on HN
- 7Re-Introducing Hash Indexes in PostgreSQL4 people · 23 points · first shared 2021 · discussion on HN
- 8Fastest Way to Read Excel in Python4 people · 14 points · first shared 2024 · 1 comments on HN
- 9An unexpected find that freed 20GB of unused index space in PostgreSQL3 people · 377 points · first shared 2021 · 78 comments on HN
- 10Fast Way to Load Data into PostgreSQL Using Python3 people · 251 points · first shared 2019 · 29 comments on HN
- 11Exhaustiveness Checking with Mypy3 people · 88 points · first shared 2020 · 19 comments on HN
- 12Handling Concurrency Without Locks3 people · 15 points · first shared 2022 · 1 comments on HN
- 13Common Mistakes and Missed Optimization Opportunities in SQL2 people · 183 points · first shared 2019 · 66 comments on HN
- 14How to Get Foreign Keys Horribly Wrong2 people · 71 points · first shared 2025 · 34 comments on HN
- 15One Database Transaction Too Many – Haki Benita2 people · 7 points · first shared 2021 · discussion on HN
What HN people wrote when they linked it
msdrigg · 2026-01-20
The most interesting thing for me in this article was the mention of `MERGE` almost in passing at the end.
> I'm not a big fan of using the constraint names in SQL, so to overcome both limitations I'd use MERGE instead:
```
db=# MERGE INTO urls t
USING (VALUES (1000004, 'https://hakibenita.com')) AS s(id, url)
ON t.url = s.url
WHEN MATCHED THEN UPDATE SET id = s.id
WHEN NOT MATCHED THEN INSERT (id, url) VALUES (s.id, s.url);
MERGE 1
```
I use `insert ... on conflict do update ...` all the time to handle upserts, but it seems like merge may be more powerful and able to work in more scenarios. I hadn't heard of it before.
simonw · 2021-07-17
So many - I'm going to share a few that come to mind quickly, rather than agonizing over picking the best possible results.
Tim Bray: https://www.tbray.org/ongoing/
Julia Evans: https://jvns.ca/
Tom MacWright: https://macwright.com/
Jacob Kaplan-Moss: https://jacobian.org/
Lea Verou: https://lea.verou.me/
Will Larson: https://lethain.com/featured/
Camille Fournier: https://skamille.medium.com/
Haki Benita: https://hakibenita.com/
Brandur Leach: https://brandur.org/articles
vanflymen · 2026-04-13
I've been building in Django for a long time and I love the framework, but some things have always bugged me:
- Typing is exceptionally painful...and getting proper type safety across a Django project is a fight
- the ORM leaks throughout your project: it's easy to chain ORM methods in a view, a serializer, a template tag, a Celery task, etc and N+1s end up being difficult to find
- models become god objects: custom managers, save overrides, signal handlers, computed properties, validation logic, your models attract everything until they're 500 lines of tangled responsibilities.
- tests become slow over time because you either hit the DB for everything or mock too aggressively: this is because there's no clear encapsulated layers.
and a ton of other stuff including aesthetics...
Anyway, the short version of my solution is to push all ORM work into repositories that return Pydantic DTOs. Services get repos via constructor injection and contain pure business logic with zero model imports. Your views become one-liners. Every layer boundary is typed, and each layer is independently testable.
Some of the opinions:
- Prefixed ULID primary keys: double-clickable Stripe-style IDs like ord_01jq3v... on every model.
- Models are dumb but aesthetic: Meta first, fields in a strict order (identifiers, times, status, domain, relations), all indexes and constraints declared explicitly in Meta, zero business logic.
- Repositories return DTOs, never querysets so the ORM never leaks…
dataviz1000 · 2021-05-05
Lately, I've been studying machine learning, from point zero, with a focus on time series analysis. Two months in already completed a course on Python and another book on Pandas, several hundreds of hours later in the fourth chapter in a book I paid for on deep learning and time series analysis they provided me with the most important information I needed: there is no evidence deep learning works better than traditional statistical analysis using classical methods like SARIMA and ETS. Sure, great, if an academic is interested in theory hopefully making a breakthrough, however, the rest of us who are interested in applied should stick with the classical methods.
I was going to write a lot here but I'll keep it short.
What I discovered is that everything I want to do can best be done in PostgreSQL. It's one thing to do data analysis in a Python notebook and another in an environment that works dynamically on a server. My first guess was to do the heavy lifting in Python with Numpy, Pandas, and machine learning and have the node server -- instead of Django and if I'm learning a new web framework it will be Phoenix -- execute the Python scripts through stdin / stdout. Since started I learned that I don't need machine learning and that I can do the calculations inside PostgreSQL sometimes orders of magnitude faster than in Python.
I'm using TimescaleDB which provides the postgresSQL time_bucket() function and with chunks should scale very well. First I tried to integrate it…
haki · 2026-02-05
Some a prime example of a service that naturally peaks at round hours.
We have a habbit of never scheduling long running processes at round hours. Usually because they tend to be busier.
https://hakibenita.com/sql-tricks-application-dba#dont-schedule-long-r…
bkovacev · 2025-01-15
Old is a harsh word, maybe mature would be a better fit, not everything new and shiny is gold, and yet not everything old sucks.
Not arguing here about types and Pydantic being faster than the built in ModelSerializers. However, for serializer speed improvements and performance in DRF I would advise dropping ModelSerializers and either going for Serializers or plain dict. Haki Benita has a beautiful article on that [0]. I was able to accomplish sub 200 response times on a fairly large response from tables that had tens of millions of records.
I think you have no objective reason other than your styling and rather personal preference for function based views?
[0] - https://hakibenita.com/django-rest-framework-slow
niklasd · 2023-01-01
I found this explanation very helpful: https://hakibenita.com/python-dependency-injection (if you program in Python)
aledalgrande · 2022-08-16
I don't know if it's the same for MySQL, but in Postgres a CTE would possibly force the planner to make less efficient choices [1]. Would be interesting if someone who knows more than me could clarify on this
[1] https://hakibenita.com/be-careful-with-cte-in-postgre-sql
irrational · 2022-02-10
Here is an another article that expands on how Postgres stores text data.
https://hakibenita.com/sql-medium-text-performance
gavinray · 2021-11-09
I want to stress the importance of not using
id int SERIAL
If you are on a somewhat recent version of postgres, please do yourself a favor and use:
id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
An "identity column", the part here:
https://hakibenita.com/postgresql-unknown-features#prevent-setting-the…
You might think this is trivial -- but SERIAL creates an "owned" (by a certain user) sequence behind the scenes, and so you run into massive headaches if you try to move things around.
Identity columns don't, and avoid the issue altogether.
https://www.2ndquadrant.com/en/blog/postgresql-10-identity-columns/
c2h5oh · 2021-04-10
CTE advice is somewhat questionable, as it is database specific.
CTEs were for a very long time an optimization fence in PostgreSQL, were not inlined and behaved more like temporary materialized views.
Only with release of PostgreSQL 12 some CTE inlining is happening - with limitations: not recursive, no side-effects and are only referenced once in a later part of a query.
Mode info: https://hakibenita.com/be-careful-with-cte-in-postgre-sql
jamestimmins · 2021-02-02
OP has a pretty interesting writeup about why DRF model serialization is slow. https://hakibenita.com/django-rest-framework-slow
chrismeller · 2021-02-01
He actually mentioned index de-duplication earlier: https://hakibenita.com/postgresql-unused-index-size#activating-b-tree-…
If I had to guess, I would say that it doesn't accomplish anything (or as much as you'd think) for null values simply because there is no real data to store in either approach, you just have a bunch of pointers either way.
dd82 · 2020-08-15
You might be interested in https://hakibenita.com/django-rest-framework-slow. If you weren't able to update to a 3.x version that has https://github.com/django/django/commit/a2c31e12da272acc76f3a3a0157fae… this might have bit you pretty hard.
gobblegobble2 · 2021-11-09
The feed is here: https://hakibenita.com/feeds/all.atom.xml.