Skip to main content

Command Palette

Search for a command to run...

The Hidden Danger of GROUP BY in MySQL: How PostgreSQL Saves You

Updated
4 min read
The Hidden Danger of GROUP BY in MySQL: How PostgreSQL Saves You
S

I’m a software developer passionate about exploring new technologies and continuously learning. On my blog, I share what I discover—whether it’s cool tricks, coding solutions, or interesting tools. My goal is to document my journey and help others by sharing insights that I find useful along the way. Join me as I write about: Programming tips & tricks Lessons from everyday coding challenges Interesting tools & frameworks I come across Always curious, always learning—let’s grow together! 🚀

Have you ever written a GROUP BY query, got results, and thought,

"Hmm... something feels weird, but hey! it runs right?"

If yes congratulations, you’ve stumbled upon one of SQL’s most interesting (and dangerous) traps: loose GROUP BY behavior especially in MySQL.

Today, let's explore why MySQL’s GROUP BY can behave differently from PostgreSQL (and even from what SQL standards expect), and why understanding this could save your server / app from subtle and really hard-to-detect bugs.


🧠 SQL Standards: What Should Happen

According to the SQL Standard (aka ANSI SQL):

  • If you GROUP BY some columns,

  • Then every column you select must either:

    • Appear in the GROUP BY, or

    • Be wrapped inside an aggregate function like SUM(), MIN(), MAX(), etc.

This rule makes a lot of sense.
When you’re grouping rows, you’re collapsing them together.
You can’t just pluck random values from them without a clear instruction.

for example:

let’s imagine a simple table called world where a country is mapped to it’s continent’s name

Table:

countrycontinent
IndiaAsia
GermanyEurope
BelgiumEurope

🔥 Enter: MySQL’s Loose Mode

Historically, MySQL had different priorities:

  • Focus on speed ✅

  • Make things easy for beginners ✅

  • Allow queries to work even if they technically violate standards 😬

In older MySQL versions (pre-5.7), you could write queries that didn’t strictly follow the rules and MySQL would just... do something. 😅

Example:

SELECT country FROM world GROUP BY continent;

👉 What happens here?

  • MySQL groups the rows by continent (fine).

  • Then, for the country field (which isn’t aggregated),
    it picks a random value from the group!

Yes!! totally random.
Depending on indexes, engine decisions, or even sheer luck, you could get any country’s name for that continent.


🎯 Real-Life Danger Example

Suppose you’re showing "representative countries" on a dashboard.

You write:

SELECT continent, country FROM world GROUP BY continent;
  • One day, Europe shows "Germany."

  • Next week, Europe shows "Belgium."

  • After a migration, Europe shows "Croatia."

The code didn’t change. The query didn’t change. The data didn't change. Yet your users see different results. 🤯


⚡ PostgreSQL’s Strict Mode (and New MySQL Strict Mode)

PostgreSQL said: Nope. Not allowed.
If you try that loose query, PostgreSQL immediately errors out:

ERROR: column "world.country" must appear in the GROUP BY clause or be used in an aggregate function LINE 1: SELECT country FROM world GROUP BY continent;

✅ PostgreSQL forces you to either:

  • Add country into the GROUP BY, or

  • Apply an aggregation like MIN(country), MAX(country), etc.

MySQL also eventually realized the dangers of loose mode.
In MySQL 5.7 and 8.0, strict SQL compliance is now possible with this setting:

SET sql_mode = 'ONLY_FULL_GROUP_BY';

When ONLY_FULL_GROUP_BY is enabled:

  • MySQL behaves like PostgreSQL.

  • It rejects bad queries that select non-grouped, non-aggregated fields.

  • Your queries become predictable, correct, and portable.

(And yes, modern MySQL has this strict mode ON by default from MySQL 8.0 but always double-check it.)


🛠️ How to Fix It: Proper GROUP BY Queries

Here’s how you write that dashboard query properly:

SELECT continent, MIN(country) as first_country
FROM world
GROUP BY continent;
  • Now, you explicitly tell SQL: Pick the lexicographically smallest country name from each continent.

  • Deterministic ✅

  • Standards-compliant ✅

  • Works reliably across MySQL, PostgreSQL, and others ✅


🧩 Quick Checklist: GROUP BY Best Practices

✅ Every non-aggregated column in SELECT must appear in GROUP BY.
✅ Always think: "Am I grouping this value explicitly?"
✅ Turn on ONLY_FULL_GROUP_BY in MySQL (especially for production).
✅ Test your queries on strict SQL databases like PostgreSQL to catch hidden bugs early.


📚 References and Further Reading


Loose GROUP BY might seem like a harmless shortcut...
Until one day it quietly ships wrong data to production. 😅

So next time you group rows, remember:

"Group your columns wisely or risk grouping your bugs unknowingly." 🚀

Tech Diaries

Part 2 of 3

A candid series where I document real-world tech hiccups, debugging adventures, and those “what the heck just happened?” moments we all face as developers. No fluff just raw, relatable tech stories, learnings, and laughs along the way.

Up next

The Bridge That Broke My Internet💔

We all would’ve studied about different types of connections in Computer Networks. One of them is the humble bridge connection. But what if I told you this innocent-looking "Bridge Connection" actually broke my internet and had me scratching my head ...