È 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)
.
Il tuo campo ID è unico? –
@Pekka: credi che sia possibile ottenere collisione UUID nel nostro universo? ;-) – zerkms
@zerkms no :) ma IIRC, 'LAST_INSERT_ID()' funziona solo su colonne univoche, da qui la mia domanda –