Domanda

Mi chiedo se c'è un "migliore" scelta per il confronto in MySQL per un generale sito in cui non si è sicuri al 100% di quello che sarà entrato?Capisco che tutte le codifiche dovrebbe essere lo stesso, come MySQL, Apache, HTML e nulla all'interno di PHP.

In passato ho impostato PHP per la produzione di "UTF-8", ma che di confronto di questo match in MySQL?Sto pensando che è una di quelle UTF-8, ma ho usato utf8_unicode_ci, utf8_general_ci, e utf8_bin prima.

È stato utile?

Soluzione

La differenza principale è l'ordinamento precisione (quando si confrontano i caratteri in lingua) e le prestazioni. L'unico particolare è utf8_bin che è per il confronto caratteri in formato binario.

utf8_general_ci è un po 'più veloce di utf8_unicode_ci, ma meno preciso (per l'ordinamento). Il codifica utf8 linguaggio specifico (come utf8_swedish_ci) contengono norme di lingua aggiuntivi che li rendono il più preciso per ordinare per quelle lingue. La maggior parte del tempo che uso <=> (io preferisco la precisione a piccoli miglioramenti delle prestazioni), a meno che non ho un buon motivo per preferire una lingua specifica.

Si può leggere di più su specifici caratteri Unicode imposta sul manuale di MySQL - http://dev.mysql.com/doc/refman/5.0/en/charset-unicode-sets.html

Altri suggerimenti

Essere molto, molto consapevoli di questo problema che può verificarsi quando si utilizza utf8_general_ci.

MySQL non distinguere tra alcuni caratteri in alcune dichiarazioni, se si utilizza il utf8-bin collazione. Questo può portare a bug molto brutto - soprattutto per esempio, quando si tratta di nomi utente. A seconda dell'implementazione che utilizza le tabelle del database, questo problema potrebbe consentire a utenti malintenzionati di creare un nome utente corrispondente un account amministratore.

Questo problema si espone per lo meno nelle prime versioni 5.x -. Non sono sicuro se questo comportamento come modificato in seguito

Non sono un amministratore di database, ma per evitare questo problema, vado sempre con <=> invece di un case-insensitive.

Lo script che segue descrive il problema con l'esempio.

-- first, create a sandbox to play in
CREATE DATABASE `sandbox`;
use `sandbox`;

-- next, make sure that your client connection is of the same 
-- character/collate type as the one we're going to test next:
charset utf8 collate utf8_general_ci

-- now, create the table and fill it with values
CREATE TABLE `test` (`key` VARCHAR(16), `value` VARCHAR(16) )
    CHARACTER SET utf8 COLLATE utf8_general_ci;

INSERT INTO `test` VALUES ('Key ONE', 'value'), ('Key TWO', 'valúe');

-- (verify)
SELECT * FROM `test`;

-- now, expose the problem/bug:
SELECT * FROM test WHERE `value` = 'value';

--
-- Note that we get BOTH keys here! MySQLs UTF8 collates that are 
-- case insensitive (ending with _ci) do not distinguish between 
-- both values!
--
-- collate 'utf8_bin' doesn't have this problem, as I'll show next:
--

-- first, reset the client connection charset/collate type
charset utf8 collate utf8_bin

-- next, convert the values that we've previously inserted in the table
ALTER TABLE `test` CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin;

-- now, re-check for the bug
SELECT * FROM test WHERE `value` = 'value';

--
-- Note that we get just one key now, as you'd expect.
--
-- This problem appears to be specific to utf8. Next, I'll try to 
-- do the same with the 'latin1' charset:
--

-- first, reset the client connection charset/collate type
charset latin1 collate latin1_general_ci

-- next, convert the values that we've previously inserted
-- in the table
ALTER TABLE `test` CONVERT TO CHARACTER SET latin1 COLLATE latin1_general_ci;

-- now, re-check for the bug
SELECT * FROM test WHERE `value` = 'value';

--
-- Again, only one key is returned (expected). This shows 
-- that the problem with utf8/utf8_generic_ci isn't present 
-- in latin1/latin1_general_ci
--
-- To complete the example, I'll check with the binary collate
-- of latin1 as well:

-- first, reset the client connection charset/collate type
charset latin1 collate latin1_bin

-- next, convert the values that we've previously inserted in the table
ALTER TABLE `test` CONVERT TO CHARACTER SET latin1 COLLATE latin1_bin;

-- now, re-check for the bug
SELECT * FROM test WHERE `value` = 'value';

--
-- Again, only one key is returned (expected).
--
-- Finally, I'll re-introduce the problem in the exact same 
-- way (for any sceptics out there):

-- first, reset the client connection charset/collate type
charset utf8 collate utf8_generic_ci

-- next, convert the values that we've previously inserted in the table
ALTER TABLE `test` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;

-- now, re-check for the problem/bug
SELECT * FROM test WHERE `value` = 'value';

--
-- Two keys.
--

DROP DATABASE sandbox;

In realtà, probabilmente si desidera utilizzare utf8_unicode_ci o utf8_general_ci.

  • <=> tipi di strappando via tutti gli accenti e l'ordinamento, come se fosse ASCII
  • <=> utilizza l'ordinamento Unicode, in modo che ordina correttamente in più lingue

Tuttavia, se si utilizzano solo questo per memorizzare il testo inglese, questi non dovrebbero differire.

E 'meglio usare set di caratteri utf8mb4 con le regole di confronto utf8mb4_unicode_ci.

Il set di caratteri, utf8, supporta solo una piccola quantità di punti di codice UTF-8, circa il 6% dei caratteri possibili. ROW_FORMAT=DYNAMIC supporta solo il Basic Multilingual Plane (BMP). Ci 16 altri piani. Ogni piano contiene 65.536 caratteri. Barracuda supporta tutti i 17 aerei.

MySQL troncherà 4 byte caratteri UTF-8 con conseguente dati danneggiati.

Il set di caratteri Antelope è stata introdotta in MySQL 5.5.3 2010-03-24.

Alcune delle modifiche necessarie per utilizzare il nuovo set di caratteri non sono banali:

    possono avere bisogno di
  • Le modifiche da apportare nella scheda database dell'applicazione.
  • dovranno essere fatti per my.cnf, tra cui l'impostazione del set di caratteri, il confronto e il passaggio innodb_file_format al Barracuda
  • modifiche
  • Istruzioni CREATE
  • SQL potrebbe essere necessario includere: innodb_file_format_max
    • DYNAMIC è richiesto per gli indici su VARCHAR (192) e più grandi.

NOTA: Il passaggio a innodb_file_format = barracuda da INDEX contact_idx (contact), può richiedere il riavvio del servizio MySQL più di una volta. contact non cambia fino a dopo il servizio MySQL è stato riavviato a:. VARCHAR(128)

MySQL utilizza il vecchio INSERT formato di file InnoDB. last supporta i formati di fila dinamici, che sarà necessario se non si vuole colpire gli errori SQL per la creazione di indici e le chiavi dopo si passa al charset: \PDO::MYSQL_ATTR_INIT_COMMAND

  • # 1709 - Indice dimensioni delle colonne troppo grande. La dimensione massima della colonna è 767 byte.
  • # 1071 - specificata chiave è stata troppo a lungo; massima lunghezza della chiave è 767 byte

Lo scenario seguente è stato testato con MySQL 5.6.17: Per impostazione predefinita, MySQL è configurato in questo modo:

SHOW VARIABLES;

innodb_large_prefix = OFF
innodb_file_format = Antelope

interrompere il servizio MySQL e aggiungere le opzioni alla vostra my.cnf esistente:

[client]
default-character-set= utf8mb4

[mysqld]
explicit_defaults_for_timestamp = true
innodb_large_prefix = true
innodb_file_format = barracuda
innodb_file_format_max = barracuda
innodb_file_per_table = true

# Character collation
character_set_server=utf8mb4
collation_server=utf8mb4_unicode_ci

Esempio SQL CREATE:

CREATE TABLE Contacts (
 id INT AUTO_INCREMENT NOT NULL,
 ownerId INT DEFAULT NULL,
 created timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
 modified timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 contact VARCHAR(640) NOT NULL,
 prefix VARCHAR(128) NOT NULL,
 first VARCHAR(128) NOT NULL,
 middle VARCHAR(128) NOT NULL,
 last VARCHAR(128) NOT NULL,
 suffix VARCHAR(128) NOT NULL,
 notes MEDIUMTEXT NOT NULL,
 INDEX IDX_CA367725E05EFD25 (ownerId),
 INDEX created (created),
 INDEX modified_idx (modified),
 INDEX contact_idx (contact),
 PRIMARY KEY(id)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB ROW_FORMAT=DYNAMIC;
  • Si può vedere l'errore # 1709 generato per <=> se <=> viene rimosso dal l'istruzione CREATE.

NOTA: La modifica l'indice di limitare ai primi 128 caratteri sul <=> elimina la necessità di utilizzare Barracuda con <=>

INDEX contact_idx (contact(128)),

Si noti inoltre: quando si dice che la dimensione del campo è <=>, che non è 128 byte. È possibile utilizzare avere 128, 4 caratteri byte o 128, 1 caratteri byte.

Questa dichiarazione <=> dovrebbe contenere il carattere 4 byte 'cacca' nella riga 2:

INSERT INTO `Contacts` (`id`, `ownerId`, `created`, `modified`, `contact`, `prefix`, `first`, `middle`, `last`, `suffix`, `notes`) VALUES
(1, NULL, '0000-00-00 00:00:00', '2014-08-25 03:00:36', '1234567890', '12345678901234567890', '1234567890123456789012345678901234567890', '1234567890123456789012345678901234567890', '12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678', '', ''),
(2, NULL, '0000-00-00 00:00:00', '2014-08-25 03:05:57', 'poo', '12345678901234567890', '💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '', ''),
(3, NULL, '0000-00-00 00:00:00', '2014-08-25 03:05:57', 'poo', '12345678901234567890', '💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '123💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '', '');

È possibile visualizzare la quantità di spazio utilizzato dalla colonna <=>:

mysql> SELECT BIT_LENGTH(`last`), CHAR_LENGTH(`last`) FROM `Contacts`;
+--------------------+---------------------+
| BIT_LENGTH(`last`) | CHAR_LENGTH(`last`) |
+--------------------+---------------------+
|               1024 |                 128 | -- All characters are ASCII
|               4096 |                 128 | -- All characters are 4 bytes
|               4024 |                 128 | -- 3 characters are ASCII, 125 are 4 bytes
+--------------------+---------------------+

Nella scheda del database, si consiglia di impostare il set di caratteri e collazione per la connessione:

SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_ci'

In PHP, questo sarebbe fissati per: <=>

References:

Le regole di confronto di influenzare la modalità di ordinamento dei dati e la gestione delle stringhe rispetto ad ogni altro.Ciò significa che è necessario utilizzare le regole di confronto che la maggior parte degli utenti si aspettano.

Esempio dal documentazione:

utf8_general_ci inoltre è soddisfacente per tedesco e francese, ad eccezione di che ‘ß’ è uguale a ‘s’, e non ‘ss’.Se questo è accettabile per il vostro applicazione, allora si dovrebbe utilizzare utf8_general_ci perché è più veloce.In caso contrario, utilizzare utf8_unicode_ci perché è più preciso.

Così, dipende dal tuo numero di utenti previsto e di quanto è necessario corretto l'ordinamento.Per un utente inglese di base, utf8_general_ci dovrebbe bastare, per le altre lingue, come lo svedese, speciali regole di confronto sono state create.

In sostanza, dipende da come si pensa di una stringa.

Io uso sempre utf8_bin a causa del problema evidenziato da Guus. A mio parere, per quanto riguarda la banca dati dovrebbe essere interessato, una stringa è ancora solo una stringa. Una stringa è un numero di caratteri UTF-8. Un personaggio ha una rappresentazione binaria allora perché ci si deve conoscere la lingua che si sta utilizzando? Di solito, le persone saranno costruendo basi di dati per i sistemi con la possibilità di siti multilingue. Questo è il punto di utilizzare UTF-8 come un set di caratteri. Sono un po 'di un pureist ma penso che i rischi di bug pesantemente superano il leggero vantaggio che si può ottenere su indicizzazione. Tutte le regole relative lingua dovrebbe essere fatto ad un livello molto più elevato rispetto al DBMS.

Nei miei libri "valore" non dovrebbero mai in un milione di anni essere uguale a "valore".

Se voglio memorizzare un campo di testo e fare un caso di ricerca insensibile, userò le funzioni di stringa MySQL con funzioni PHP, come LOWER () e la funzione PHP strtolower ().

Per informazioni UTF-8 testuale, si dovrebbe utilizzare utf8_general_ci perché ...

  • utf8_bin: confronta le stringhe da parte del valore binario di ogni carattere la stringa

  • <=>: confrontare le stringhe utilizzando le regole di lingua generale e utilizzando i confronti case-insensitive

a.k.a. si dovrebbe rendere la ricerca e l'indicizzazione dei dati più veloce / più efficiente / più utile.

La risposta accettata suggerisce abbastanza definitivamente utilizzando utf8_unicode_ci, e mentre per i nuovi progetti che è grande, ho voluto mettere in relazione la mia recente esperienza contrario nel caso in cui si salva nessuno po 'di tempo.

A causa utf8_general_ci è il confronto predefinito per Unicode in MySQL, se si desidera utilizzare utf8_unicode_ci poi si finisce per dover specificare in un molto di posti.

Per esempio, tutte le connessioni client non solo hanno un set di caratteri di default (che ha senso per me), ma anche un metodo di confronto di default (cioè il confronto sarà sempre default utf8_general_ci per Unicode).

Probabilmente, se si utf8_unicode_ci utilizzato per i campi, i vostri script che si connettono al database dovranno essere aggiornati per parlare della raccolta desiderata in modo esplicito -. Altrimenti query utilizzando stringhe di testo può fallire quando la connessione sta usando il confronto predefinito

Il risultato è che quando si converte un sistema esistente di qualsiasi dimensione a Unicode / UTF8, si può finire per essere costretti a utilizzare utf8_general_ci a causa del modo in cui MySQL gestisce le impostazioni predefinite.

Per il caso evidenziato da Guus, vi consiglierei vivamente utilizzando utf8_unicode_cs (maiuscole e minuscole, corrispondenza rigorosa, ordinando in modo corretto per la maggior parte) invece di utf8_bin (corrispondenza rigorosa, ordinamento errato).

Se il campo è destinato ad essere cercato, al contrario di abbinati per un utente, quindi utilizzare utf8_general_ci o utf8_unicode_ci. Entrambi sono case-insensitive, uno sarà losely corrisponde ( ‘ß’ è uguale a ‘s’, e non ‘ss’). Ci sono anche le versioni in lingua originale, come utf8_german_ci dove l'abbinamento perdere è più adatto per la lingua specificata.

[Modifica - quasi 6 anni più tardi]

Vi consiglio non è più il carattere "utf8" impostato su MySQL, e invece consiglio il set di caratteri "utf8mb4". Essi corrispondono quasi interamente, ma consentono un po '(molto) più caratteri Unicode.

Realisticamente, MySQL dovrebbe hanno aggiornato il set "utf8" carattere e rispettive regole di confronto per abbinare la specificazione "utf8", ma, invece, un carattere distinto set e le rispettive regole di confronto da non avere un impatto designazione di stoccaggio per quelli già usando il loro utf8 incompleta" "set di caratteri.

Ho trovato queste tabelle di confronto utile. http://collation-charts.org/mysql60/ . Io non sono sicuro di quale sia l'utf8_general_ci usato però.

Ad esempio ecco la tabella per utf8_swedish_ci. Essa mostra quali caratteri interpreta come la stessa. http://collation-charts.org/mysql60/mysql604.utf8_swedish_ci.html

Nel file di caricamento del database, aggiungere la seguente riga prima di ogni riga:

SET NAMES utf8;

E il problema dovrebbe essere risolto.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top