SQL differences

GROUP BY Counts Berlin and berlin as One City on MySQL and Two on PostgreSQL

A report counts sign-ups per city. On PostgreSQL and SQLite, Berlin and berlin are two rows. On MySQL and MariaDB they are one, and the count beside it is the sum of both.

MySQL and MariaDB compare text with a case-insensitive collation by default (see Text comparisons and letter case), and grouping and removing duplicates use the same comparison. The spelling that stands for a group is the one the engine happened to see first. On SQLite and PostgreSQL each spelling is its own group.

SELECT COUNT(DISTINCT s) FROM t, run on four engines: SQLite 3.45.1 3; PostgreSQL 16.15 3; MySQL 8.0.46 2; MariaDB 10.11.14 2.

What Each Engine Answered

On a table made with

CREATE TABLE t (s VARCHAR(10));
INSERT INTO t VALUES ('x'), ('X'), ('y');

the example

SELECT COUNT(DISTINCT s) FROM t;

gave these answers:

Engine Answer
SQLite 3.45.1 3
PostgreSQL 16.15 3
MySQL 8.0.46 2
MariaDB 10.11.14 2

Every Form BareSQL Checks

BareSQL Portable checks 4 forms of this, and each was run on all four engines. yes means the engine ran it and the engines that ran it agreed, no means the engine refused it, and a value is what the engine answered where they disagreed.

Form SQLite PostgreSQL MySQL MariaDB
GROUP BY 3 3 2 2
DISTINCT 3 3 2 2
COUNT(DISTINCT) 3 3 2 2
UNION 4 4 2 2

What to Write Instead

This form answers the same on every engine:

SELECT COUNT(DISTINCT LOWER(s)) FROM t;
Engine Answer
SQLite 3.45.1 2
PostgreSQL 16.15 2
MySQL 8.0.46 2
MariaDB 10.11.14 2

Group on LOWER(s) to merge spellings on every engine, as in SELECT LOWER(s), COUNT(*) FROM t GROUP BY LOWER(s). To keep them apart on MySQL and MariaDB instead, give the column a binary collation there.

What BareSQL Portable Reports

Saved as example.sql, the example gets this from baresql lint --all:

$ baresql lint --all example.sql
example.sql:3:8: info: GROUP BY, DISTINCT, COUNT(DISTINCT) and UNION treat 'x' and 'X' as one value on MySQL and MariaDB and as two on SQLite and PostgreSQL. [group-case]
    COUNT(DISTINCT s)
    Group on LOWER(s) to merge spellings on every engine, as in SELECT LOWER(s), COUNT(*) FROM t GROUP BY LOWER(s). To keep them apart on MySQL and MariaDB instead, give the column a binary collation there.

1 problem: 1 note.

baresql rules group-case prints the same answers in the terminal, and the BareSQL Portable page lists every difference BareSQL knows.