No UNIQUE
column required, but a unique ordering must be possible
This method requires no single unique column but it requires a unique order. This is effectively the window version of rank()
and not row_number()
; however, rank()
is row_number()
if you can get a unique ordering. I based this approach on SIMULATING ROW NUMBER IN POSTGRESQL PRE 8.4 by Leo Hsu and Regina Obe, it's listed as the "cross-platform method."
SELECT (
SELECT COUNT(*) FROM people
WHERE
(COALESCE(people.last_name,'') || COALESCE(people.first_name,'')) <=
(COALESCE(oldtable.last_name,'') || COALESCE(oldtable.first_name,''))
) AS row_number,
oldtable.*
FROM people AS oldtable
ORDER BY oldtable.last_name, oldtable.first_name;
In the comments of that page, we can find a slightly simplified version made pg-specific with
SELECT (
SELECT COUNT(*) FROM people
WHERE
ROW(people.last_name, people.first_name) <=
ROW(oldtable.last_name,first_name)
) AS row_number,
oldtable.*
FROM people AS oldtable
ORDER BY oldtable.last_name, oldtable.first_name;
There really is no elegant way to break this down... We have a
SELECT oldtable.*
FROM people AS oldtable
ORDER BY oldtable.last_name, oldtable.first_name;
Now we're going to do a correlated subquery in the SELECT
that compares how many rows are <=
the current row.
- In the first example, we do the comparison with the database-agnostic method of concatenating columns to create something that should likely makes unique.
- In the second example, we do the comparison with the PostgreSQL specific
ROW()
constructor
It's not an easy query to break down, but we can construct a simpler table.
CREATE TABLE foobar AS
SELECT x FROM generate_series(1,10)
AS t(x) ORDER BY random();
SELECT
x,
(SELECT count(*) FROM foobar AS f2 WHERE f2.x <= f1.x)
FROM foobar AS f1
ORDER BY x;
In this example we again
- take an unordered set that provides for a unique ordering
- order the set
- compare it with itself to see how many rows are
<=
the current row.
Here would be the output of the above simplified example. We're only simplifying by providing a single column with a unique order (rather than composite-ordering, and bypassing the protection against nulls). As another aside, if rows are unique by a left-to-right ordering of the columns, we can compare ROW(people.*) <= ROW(oldtable.*)
x | count
----+-------
1 | 1
2 | 2
3 | 3
4 | 4
5 | 5
6 | 6
7 | 7
8 | 8
9 | 9
10 | 10