Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PostgreSQL - sort by UUID version 1 timestamp

I am using UUID version 1 as the primary key. I would like to sort on UUID v1 timestamp. Right now if I do something like this:

SELECT id, title 
FROM table 
ORDER BY id DESC;

PostgreSQL does not sort records by UUID timestamp, but by UUID string representation, which ends up with unexpected sorting result in my case.

Am I missing something, or there is not a built in way to do this in PostgreSQL?

like image 353
user232343 Avatar asked Jun 08 '16 21:06

user232343


People also ask

Can you order by UUID?

There is no universal ordering of UUIDs (or even byte layout). However, PostgreSQL has an implementation specific UUID ordering, as does SQL Server (and this is not guaranteed to be the same; .

Does PostgreSQL support UUID?

PostgreSQL allows you store and compare UUID values but it does not include functions for generating the UUID values in its core. Instead, it relies on the third-party modules that provide specific algorithms to generate UUIDs.

What is UUID-OSSP?

The uuid-ossp module provides functions to generate universally unique identifiers (UUIDs) using one of several standard algorithms. There are also functions to produce certain special UUID constants. This module is only necessary for special requirements beyond what is available in core PostgreSQL.

What is uuid_generate_v4?

The PostgreSQL UUID data type is used to store the UUID values for a specified column. We can use the CREATE Extension command to install the uuid-ossp module to creates the UUID values. We can use the uuid_generate_v4() functionto automatically retrieve the UUID values for the table's particular column.


1 Answers

The timestamp is one of the parts of a v1 UUID. It is stored in hex format as hundreds nanoseconds since 1582-10-15 00:00. This function extracts the timestamp:

create or replace function uuid_v1_timestamp (_uuid uuid)
returns timestamp with time zone as $$

    select
        to_timestamp(
            (
                ('x' || lpad(h, 16, '0'))::bit(64)::bigint::double precision -
                122192928000000000
            ) / 10000000
        )
    from (
        select
            substring (u from 16 for 3) ||
            substring (u from 10 for 4) ||
            substring (u from 1 for 8) as h
        from (values (_uuid::text)) s (u)
    ) s
    ;

$$ language sql immutable;

select uuid_v1_timestamp(uuid_generate_v1());
       uuid_v1_timestamp       
-------------------------------
 2016-06-16 12:17:39.261338+00

122192928000000000 is the interval between the start of the Gregorian calendar and the Unix timestamp.

In your query:

select id, title
from t
order by uuid_v1_timestamp(id) desc

To improve performance an index can be created on that:

create index uuid_timestamp_ndx on t (uuid_v1_timestamp(id));
like image 57
Clodoaldo Neto Avatar answered Oct 19 '22 11:10

Clodoaldo Neto