2011-02-08 9 views
7

ho un tipo di campo tavolo varchar (36) e voglio generare dinamicamente da MySQL così ho usato questo codice:Prendi l'uuid generato dopo inserto php

$sql_code = 'insert into table1 (id, text) values (uuid(),'some text');'; 
mysql_query($sql_code); 

come posso recuperare il generato uuid subito dopo aver inserito il record?

+1

Il tuo campo ID è unico? –

+0

@Pekka: credi che sia possibile ottenere collisione UUID nel nostro universo? ;-) – zerkms

+0

@zerkms no :) ma IIRC, 'LAST_INSERT_ID()' funziona solo su colonne univoche, da qui la mia domanda –

risposta

20
  1. char(36) è meglio
  2. non si può. L'unica soluzione è quella di eseguire 2 domande separate:

    • SELECT UUID()
    • INSERT INTO table1 (id, text) VALUES ($uuid, 'text')

dove $ UUID è il valore recuperato il 1 ° gradino.

+0

grazie ha funzionato come voglio – Star

+3

char (36) non è molto meglio, binario (16) sarebbe più efficiente – pospi

+0

@pospi: char (36) è meglio di varchar. E con binario si verificano problemi sulla confronti di stringhe (binario non rispetta le regole di confronto) – zerkms

-1

A seconda di come viene implementata la funzione uuid(), si tratta di una pratica di programmazione molto negativa: se si tenta di eseguire questa operazione con la registrazione binaria attivata (ad esempio in un cluster), l'inserto sarà most likely fail. Il suggerimento di Ivan sembra che potrebbe risolvere il problema immediato, tuttavia ho pensato che questo restituisse solo il valore generato per un campo di incremento automatico, infatti è what the manual says.

Inoltre, qual è il vantaggio dell'uso di uuid()? È molto costoso da generare, richiede molto spazio di archiviazione, aumenta il costo dell'interrogazione dei dati e non è crittograficamente sicuro. Utilizzare invece un generatore di sequenza o un autoincremento.

Indipendentemente se si utilizza un generatore di sequenza o uuid, se è necessario utilizzare questo come unica chiave univoca nel database, sarà necessario assegnare il valore per primo, rileggerlo in phpland e incorporare/associare il valore come un letterale alla successiva domanda di inserimento.

+0

Beh, non è una cattiva pratica. È solo un altro modo per risolvere un problema: http://www.codinghorror.com/blog/2007/03/primary-keys-ids-versus-guids.html – zerkms

+0

E come diavolo PK è correlato alla crittografia ?! – zerkms

+0

E, naturalmente, la registrazione binaria non causerà alcun problema con uuid, sia nel cluster che nella replica. – zerkms

10

È possibile eseguire tutto ciò che è necessario con trigger SQL. Il seguente SQL aggiunge un trigger su tablename.table_id per creare automaticamente l'UUID chiave primaria quando si inserisce, quindi memorizza l'ID appena creato in una variabile SQL per il successivo recupero:

CREATE TRIGGER `tablename_newid` 
AFTER INSERT ON `tablename` 
FOR EACH ROW 
BEGIN 
    IF ASCII(NEW.table_id) = 0 THEN 
     SET NEW.table_id = UNHEX(REPLACE(UUID(),'-','')); 
    END IF; 
    SET @last_uuid = NEW.table_id; 
END 

Come bonus, si inserisce l'UUID in forma binaria in un campo binario (16) per risparmiare spazio di archiviazione e aumentare notevolmente la velocità della query.

modifica: il trigger deve verificare la presenza di un valore di colonna esistente prima di inserire il proprio UUID per simulare la possibilità di fornire valori per le chiavi primarie di tabella in MySQL - senza questo, qualsiasi valore passato sarà sempre sovrascritto dal grilletto. L'esempio è stato aggiornato per utilizzare ASCII() = 0 per verificare l'esistenza del valore della chiave primaria nell'INSERT, che rileverà i valori di stringa vuota per un campo binario.

Edit 2: dopo un commento here da allora è stato mi ha fatto notare che l'uso BEFORE INSERT ha l'effetto di impostazione della variabile @last_uuid anche se l'inserto fila non riesce. Ho aggiornato la mia risposta per utilizzare AFTER INSERT - mentre ritengo che questo sia un approccio totalmente soddisfacente in circostanze generali, potrebbe avere problemi con la replica delle righe in database cluster o replicati. Se qualcuno lo sa, mi piacerebbe molto!

Per leggere indietro l'ID di inserimento della nuova riga, è sufficiente eseguire SELECT @last_uuid.

Quando l'interrogazione e la lettura di tali valori binari, le funzioni di MySQL HEX() e UNHEX() sarà molto utile, come si scrivere i valori della query in notazione esadecimale (preceduto da 0x). Il codice php-side per la risposta originale, dato questo tipo di trigger applicata alla tabella 1, potrebbe essere:

// insert row 
$sql = "INSERT INTO table1(text) VALUES ('some text')"; 
mysql_query($sql); 

// get last inserted UUID 
$sql = "SELECT HEX(@last_uuid)"; 
$result = mysql_query($sql); 
$row = mysql_fetch_row($result); 
$id = $row[0]; 

// perform a query using said ID 
mysql_query("SELECT FROM table1 WHERE id = 0x" . $id); 

seguito in risposta a @ina's comment:

Un UUID non è una stringa, anche se MySQL sceglie di rappresentarlo come tale. Sono dati binari nella sua forma grezza, e quei trattini sono semplicemente il modo amichevole di MySQL di rappresentarlo per te.

Lo spazio di archiviazione più efficiente per un UUID è di crearlo come UNHEX(REPLACE(UUID(),'-','')) - questo rimuoverà la formattazione e la riconvertirà in dati binari. Queste funzioni rendono più lento l'inserimento originale, ma tutti i confronti successivi eseguiti su quella chiave o colonna saranno molto più veloci su un campo binario a 16 byte rispetto a una stringa di 36 caratteri.

Per uno, i dati dei caratteri richiedono analisi e localizzazione. Tutte le stringhe che arrivano al motore di query vengono generalmente confrontate automaticamente con il set di caratteri del database e alcune API (wordpress viene in mente) eseguono anche CONVERT() su tutti i dati di stringa prima di eseguire una query. I dati binari non hanno questo sovraccarico. Per l'altro, il tuo char(36) sta attualmente assegnando 36 caratteri, il che significa (se il tuo database è UTF-8) ogni carattere potrebbe essere lungo come 3 or 4 bytes a seconda della versione di MySQL che stai utilizzando. Quindi uno char(36) può andare ovunque da 36 byte (se è composto interamente da caratteri ASCII bassi) a 144 se interamente composto da caratteri UTF8 di alto livello. Questo è molto più superiore ai 16 byte che abbiamo assegnato per il nostro campo binario.

Qualsiasi logica eseguita su questi dati può essere eseguita con UNHEX(), ma è meglio eseguita semplicemente eseguendo l'escape dei dati nelle query come esadecimale, con prefisso 0x. Questo è veloce come leggere una stringa, viene convertito in binario al volo e assegnato direttamente alla query o alla cella in questione. Molto veloce. La lettura dei dati è leggermente più lenta - devi chiamare HEX() su tutti i dati binari letti da una query per ottenerli in un formato utile se l'API del client non gestisce bene i dati binari (in genere il PHP in genere determinerà quel binario stringhe === null e interromperli se manipolati senza prima chiamare bin2hex(), base64_encode() o simili) - ma questo sovraccarico è minimo quanto la raccolta dei caratteri e, soprattutto, viene chiamato sulle celle effettive SELECT ed, non tutte le celle coinvolte nei calcoli interni di un risultato di una query.

Quindi, naturalmente, tutti questi piccoli aumenti di velocità sono molto minimi e altre aree si traducono in piccole riduzioni - ma quando li aggiungi tutti binary esce ancora in cima, e quando si considerano casi d'uso e le letture generali> scrive il principio che brilla davvero.

... ed è per questo che binary(16) è migliore di char(36).

+0

Non è solo la decisione di mysql di visualizzare UUID come tale, è la sua rappresentazione per raccomandazione. – zerkms

+1

Anche così, è ancora solo per la rappresentazione. Tutti questi trattini indicano le parti dell'UUID generate dal timestamp, che preservano l'unicità e che sono basate sull'indirizzo MAC della macchina host. Non c'è motivo per cui tu debba conoscere queste informazioni da un UUID, e non c'è motivo per cui tu non sia in grado di capirlo, indipendentemente dal fatto che tu conoscessi i byte offset e [la variante dell'UUID] (http://en.wikipedia.org/wiki/Uuid # Variants_and_versions) in fase di generazione. Se tutto ciò che li rimuove non fa altro che offuscare qualcuna di queste informazioni dall'essere derivata da quell'ID. – pospi

+0

"sta attualmente allocando 36 caratteri, il che significa (se il tuo database è UTF-8) 3 o 4 byte per carattere" --- è semplicemente sbagliato. La stringa UTF-8 per caratteri ascii-safe richiede esattamente 36 byte – zerkms

4

È piuttosto facile in realtà è possibile passare questo a mysql e restituirà l'id inserito.

set @id=UUID(); 
insert into <table>(<col1>,<col2>) values (@id,'another value'); 
select @id;