How to generate a random, unique, alphanumeric ID of length N in Postgres 9.6+?

后端 未结 6 595
走了就别回头了
走了就别回头了 2020-12-25 14:59

I\'ve seen a bunch of different solutions on StackOverflow that span many years and many Postgres versions, but with some of the newer features like gen_random_bytes I want

6条回答
  •  自闭症患者
    2020-12-25 15:15

    Figured this out, here's a function that does it:

    CREATE OR REPLACE FUNCTION generate_uid(size INT) RETURNS TEXT AS $$
    DECLARE
      characters TEXT := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
      bytes BYTEA := gen_random_bytes(size);
      l INT := length(characters);
      i INT := 0;
      output TEXT := '';
    BEGIN
      WHILE i < size LOOP
        output := output || substr(characters, get_byte(bytes, i) % l + 1, 1);
        i := i + 1;
      END LOOP;
      RETURN output;
    END;
    $$ LANGUAGE plpgsql VOLATILE;
    

    And then to run it simply do:

    generate_uid(10)
    -- '3Rls4DjWxJ'
    

    Warning

    When doing this you need to be sure that the length of the IDs you are creating is sufficient to avoid collisions over time as the number of objects you've created grows, which can be counter-intuitive because of the Birthday Paradox. So you will likely want a length greater (or much greater) than 10 for any reasonably commonly created object, I just used 10 as a simple example.


    Usage

    With the function defined, you can use it in a table definition, like so:

    CREATE TABLE collections (
      id TEXT PRIMARY KEY DEFAULT generate_uid(10),
      name TEXT NOT NULL,
      ...
    );
    

    And then when inserting data, like so:

    INSERT INTO collections (name) VALUES ('One');
    INSERT INTO collections (name) VALUES ('Two');
    INSERT INTO collections (name) VALUES ('Three');
    SELECT * FROM collections;
    

    It will automatically generate the id values:

        id     |  name  | ...
    -----------+--------+-----
    owmCAx552Q | ian    |
    ZIofD6l3X9 | victor |
    

    Usage with a Prefix

    Or maybe you want to add a prefix for convenience when looking at a single ID in the logs or in your debugger (similar to how Stripe does it), like so:

    CREATE TABLE collections (
      id TEXT PRIMARY KEY DEFAULT ('col_' || generate_uid(10)),
      name TEXT NOT NULL,
      ...
    );
    
    INSERT INTO collections (name) VALUES ('One');
    INSERT INTO collections (name) VALUES ('Two');
    INSERT INTO collections (name) VALUES ('Three');
    SELECT * FROM collections;
    
          id       |  name  | ...
    ---------------+--------+-----
    col_wABNZRD5Zk | ian    |
    col_ISzGcTVj8f | victor |
    

提交回复
热议问题