2013-10-23 17 views
16

Dire che ho un post simile alla tabella, che ha colonne tipiche come id, body, created_at. Mi piacerebbe generare una stringa unica con la creazione di ogni post, per l'uso in qualcosa come un url abbreviazione. Quindi forse una stringa alfanumerica di 10 caratteri. Deve essere unico all'interno della tabella, proprio come una chiave primaria.Come posso generare una stringa univoca per record in una tabella in Postgres?

Idealmente ci sarebbe un modo per Postgres per gestire entrambe queste preoccupazioni:

  1. generare la stringa
  2. Assicuriamo la sua unicità

E devono andare di pari passo, perché il mio obiettivo è quello di non dovermi preoccupare di alcun codice di applicazione dell'unicità nella mia applicazione.

+3

si può non solo usare la chiave primaria? –

+2

Voglio avere un identificatore esterno che non rivela il numero di oggetti al pubblico. –

+1

due progetti che ho trovato che risolvono il mio problema: https://github.com/inscitiv/pg_random_id https://github.com/norman/friendly_id –

risposta

3

Dai un'occhiata a un blog di Bruce. Questo ti porta parzialmente là. Dovrai assicurarti che non esista già. Forse concatene la chiave primaria?

Generating Random Data Via Sql

"mai bisogno per generare dati casuali? Si può facilmente farlo in applicazioni client e le funzioni lato server, ma è possibile generare dati casuali in SQL. La query seguente genera cinque righe di 40 -CHARACTER lunghezza minuscole stringhe alfabetiche:"

SELECT 
(
    SELECT string_agg(x, '') 
    FROM (
    SELECT chr(ascii('a') + floor(random() * 26)::integer) 
    FROM generate_series(1, 40 + b * 0) 
) AS y(x) 
) 
FROM generate_series(1,5) as a(b); 
+0

L'univocità dell'applicazione è ciò a cui sono più interessato: è facile per me generare una stringa ma è un problema programmare l'applicazione dell'unicità dal lato dell'applicazione. Ho aggiornato la mia domanda per spiegare questo un po 'più chiaramente. –

+0

Bene logicamente ci sono due modi. Controlla tutti i record esistenti. O introdurre un certo tipo di unicità garantita per la tua stringa. Il mio pensiero era di concatenare la chiave primaria alla stringa casuale. – Kuberchaun

9

non pretendo il seguente è efficiente, ma è il modo che abbiamo fatto questo genere di cose in passato.

CREATE FUNCTION make_uid() RETURNS text AS $$ 
DECLARE 
    new_uid text; 
    done bool; 
BEGIN 
    done := false; 
    WHILE NOT done LOOP 
     new_uid := md5(''||now()::text||random()::text); 
     done := NOT exists(SELECT 1 FROM my_table WHERE uid=new_uid); 
    END LOOP; 
    RETURN new_uid; 
END; 
$$ LANGUAGE PLPGSQL VOLATILE; 

make_uid() può essere utilizzato come predefinito per una colonna in my_table. Qualcosa di simile:

ALTER TABLE my_table ADD COLUMN uid text NOT NULL DEFAULT make_uid(); 

md5(''||now()::text||random()::text) può essere regolato a piacere. Potresti considerare encode(...,'base64') eccetto che alcuni dei caratteri usati in base-64 non sono compatibili con l'URL.

+2

Questo potrebbe comportare condizioni di gara? (Non ne so abbastanza sull'ambiente di runtime delle funzioni pg per pensarci attraverso ...) –

0

Utilizzare la chiave primaria nei dati. Se hai davvero bisogno di una stringa univoca alfanumerica, puoi usare la codifica base-36. In PostgreSQL puoi usare la funzione this.

Esempio:

select base36_encode(generate_series(1000000000,1000000010)); 

GJDGXS 
GJDGXT 
GJDGXU 
GJDGXV 
GJDGXW 
GJDGXX 
GJDGXY 
GJDGXZ 
GJDGY0 
GJDGY1 
GJDGY2 
5

utilizza una rete di Feistel. Questa tecnica funziona in modo efficiente per generare stringhe uniche dall'aspetto casuale in tempo costante senza alcuna collisione.

Per una versione con circa 2 miliardi di stringhe possibili (2^31) di 6 lettere, vedere this answer.

Per una versione a 63 bit basata su bigint (9223372036854775808 valori distinti), vedere this other answer.

È possibile modificare la funzione di turno come illustrato nella prima risposta per introdurre un elemento segreto per disporre di una serie di stringhe (non ipotizzabile).

2

Il modo più semplice probabilmente per utilizzare la sequenza di garantire l'unicità (quindi dopo la ss aggiungere una cifra di numeri casuali fix x):

CREATE SEQUENCE test_seq; 
CREATE TABLE test_table (
    id bigint NOT NULL DEFAULT (nextval('test_seq')::text || (LPAD(floor(random()*100000000)::text, 8, '0')))::bigint, 
    txt TEXT 
); 
insert into test_table (txt) values ('1'); 
insert into test_table (txt) values ('2'); 
select id, txt from test_table; 

Tuttavia, questo sarà sprecare un'enorme quantità di record. (Nota: il massimo bigInt è 9223372036854775807 se si utilizza il numero casuale a 8 cifre alla fine, si possono avere solo record 922337203. Probabilmente non sono necessarie 8 cifre.Verificare anche il numero massimo per l'ambiente di programmazione!)

In alternativa puoi usare varchar per l'id e persino convertire il numero precedente con to_hex() o cambiare in base36 come sotto (ma per base36, cerca di non esporlo al cliente, per evitare che si mostri qualche stringa divertente!):

PostgreSQL: Is there a function that will convert a base-10 int into a base-36 string?

+0

"sprecare una quantità enorme di record" - cosa intendi? –

+0

aggiunto qualche spiegazione – holdfenytolvaj

Problemi correlati