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

后端 未结 6 577
走了就别回头了
走了就别回头了 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:24

    Thanks to Evan Carroll answer, I took a look on hashids.org. For Postgres you have to compile the extension or run some TSQL functions. But for my needs, I created something simpler based on hashids ideas (short, unguessable, unique, custom alphabet, avoid curse words).

    Shuffle alphabet:

    CREATE OR REPLACE FUNCTION consistent_shuffle(alphabet TEXT, salt TEXT) RETURNS TEXT AS $$
    DECLARE
        SALT_LENGTH INT := length(salt);
        integer INT = 0;
        temp TEXT = '';
        j INT = 0;
        v INT := 0;
        p INT := 0;
        i INT := length(alphabet) - 1;
        output TEXT := alphabet;
    BEGIN
        IF salt IS NULL OR length(LTRIM(RTRIM(salt))) = 0 THEN
            RETURN alphabet;
        END IF;
        WHILE i > 0 LOOP
            v := v % SALT_LENGTH;
            integer := ASCII(substr(salt, v + 1, 1));
            p := p + integer;
            j := (integer + v + p) % i;
    
            temp := substr(output, j + 1, 1);
            output := substr(output, 1, j) || substr(output, i + 1, 1) || substr(output, j + 2);
            output := substr(output, 1, i) || temp || substr(output, i + 2);
    
            i := i - 1;
            v := v + 1;
        END LOOP;
        RETURN output;
    END;
    $$ LANGUAGE plpgsql VOLATILE;
    

    The main function:

    CREATE OR REPLACE FUNCTION generate_uid(id INT, min_length INT, salt TEXT) RETURNS TEXT AS $$
    DECLARE
        clean_alphabet TEXT := 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
        curse_chars TEXT := 'csfhuit';
        curse TEXT := curse_chars || UPPER(curse_chars);
        alphabet TEXT := regexp_replace(clean_alphabet, '[' || curse  || ']', '', 'gi');
        shuffle_alphabet TEXT := consistent_shuffle(alphabet, salt);
        char_length INT := length(alphabet);
        output TEXT := '';
    BEGIN
        WHILE id != 0 LOOP
            output := output || substr(shuffle_alphabet, (id % char_length) + 1, 1);
            id := trunc(id / char_length);
        END LOOP;
        curse := consistent_shuffle(curse, output || salt);
        output := RPAD(output, min_length, curse);
        RETURN output;
    END;
    $$ LANGUAGE plpgsql VOLATILE;
    
    

    How-to use examples:

    -- 3: min-length
    select generate_uid(123, 3, 'salt'); -- output: "0mH"
    
    -- or as default value in a table
    CREATE SEQUENCE IF NOT EXISTS my_id_serial START 1;
    CREATE TABLE collections (
        id TEXT PRIMARY KEY DEFAULT generate_uid(CAST (nextval('my_id_serial') AS INTEGER), 3, 'salt')
    );
    insert into collections DEFAULT VALUES ;
    

提交回复
热议问题