The interesting solution by Evan Carroll got a +1 (never even heard of percentile_disc !).

However, I have another possible solution, which has the advantage of being generic (for those RDBMS s which have CTE s - i.e. the WITH clause).

It does NOT require any special PostgreSQL specific functionality - other than the LIMIT - which has corresponding keywords in other severs.

(Complete rewrite!)

I did the following:

Created my table and data:

CREATE TABLE person ( person_id INTEGER, person_firstname VARCHAR (25), person_surname VARCHAR (25), person_age INTEGER ); INSERT INTO person VALUES ( 1, 'john', 'smith', NULL), ( 1, NULL, 'smith', 32), ( 1, NULL, NULL, NULL), ( 1, 'john', 'smith', NULL), ( 1, 'john', NULL, 32), ( 1, 'John', 'Smith', 3456); <-- note capitalised names and outlier age!

And then ran the following:

WITH fname AS ( SELECT person_id, person_firstname FROM person GROUP BY 1, 2 ORDER BY COUNT(*) DESC LIMIT 1 ), lname AS ( SELECT person_id, person_surname FROM person GROUP BY 1, 2 ORDER BY COUNT(person_surname) DESC LIMIT 1 ), age AS ( SELECT person_id, person_age FROM person GROUP BY 1, 2 ORDER BY COUNT(person_age) DESC LIMIT 1 ) SELECT fname.person_firstname, lname.person_surname, age.person_age FROM fname JOIN lname on fname.person_id = lname.person_id JOIN age on lname.person_id = age.person_id;

with the (desired) result (correct for the data):

person_firstname | person_surname | ------------------+----------------+----- john | smith | 32 <--- average good - outlier ignored! (1 row)

This solution provides the correct solution even with outliers - (see previous edits of this post for the problem!) - it does depend on the majority of answers being correct. Using the UPPER() function would also eliminate any capitalisation issues.