การเปรียบเทียบที่ดีที่สุดที่จะใช้สำหรับ MySQL กับ PHP คืออะไร? [ปิด]


731

ฉันสงสัยว่ามีตัวเลือก "ดีที่สุด" สำหรับการเปรียบเทียบใน MySQL สำหรับเว็บไซต์ทั่วไปที่คุณไม่แน่ใจ 100% ว่าจะป้อนอะไร? ฉันเข้าใจว่าการเข้ารหัสทั้งหมดควรเหมือนกันเช่น MySQL, Apache, HTML และทุกอย่างใน PHP

ในอดีตที่ผ่านมาฉันได้ตั้งค่า PHP ให้แสดงผลใน "UTF-8" แต่การเปรียบเทียบนี้เปรียบเทียบใน MySQL ที่ไหน? ฉันคิดว่ามันเป็นหนึ่งในคนที่ UTF-8 แต่ฉันได้ใช้utf8_unicode_ci, utf8_general_ciและutf8_binก่อนที่จะ


35
หมายเหตุด้านข้าง: "utf8" ของ MySQL ไม่ถูกต้อง UTF-8 (ไม่รองรับอักขระ Unicode 4+ ไบต์เช่น𝌆) อย่างไรก็ตาม "utf8mb4" นั้นเป็น ด้วย utf8 ฟิลด์จะถูกตัดทอนเมื่อแทรกเริ่มต้นด้วยอักขระ Unicode แรกที่ไม่สนับสนุน mathiasbynens.be/notes/mysql-utf8mb4
basic6

6
ฉันสงสัยว่าเราจะต้องการ 5 ไบต์สำหรับอิโมจิเหล่านั้นทั้งหมดหรือไม่ ... ถอนหายใจ
ÁlvaroGonzález

1
คำถามที่เกี่ยวข้อง: stackoverflow.com/questions/38228335/… "การเปรียบเทียบ MySQL ใดที่ตรงกับการเปรียบเทียบสตริงของ PHP"
William Entriken

สำหรับภาพรวมของตัวเลือกที่มีสติ: monolune.com/mysql-utf8-charsets-and-collations-explained
ฟลักซ์

คำตอบ:


617

ความแตกต่างที่สำคัญคือการเรียงลำดับความถูกต้อง (เมื่อเปรียบเทียบอักขระในภาษา) และประสิทธิภาพ สิ่งที่พิเศษเพียงอย่างเดียวคือ utf8_bin ซึ่งใช้สำหรับการเปรียบเทียบอักขระในรูปแบบไบนารี

utf8_general_ciค่อนข้างเร็วกว่าutf8_unicode_ciแต่มีความแม่นยำน้อยกว่า (สำหรับการจัดเรียง) การเข้ารหัสภาษา utf8 เฉพาะ (เช่นutf8_swedish_ci) มีกฎภาษาเพิ่มเติมที่ทำให้พวกเขาถูกต้องที่สุดในการเรียงลำดับสำหรับภาษาเหล่านั้น เวลาส่วนใหญ่ที่ฉันใช้utf8_unicode_ci(ฉันชอบความแม่นยำในการปรับปรุงประสิทธิภาพเล็กน้อย) เว้นแต่ว่าฉันมีเหตุผลที่ดีที่จะชอบภาษาที่เฉพาะเจาะจง

คุณสามารถอ่านเพิ่มเติมเกี่ยวกับชุดอักขระ Unicode ที่ระบุในคู่มือ MySQL - http://dev.mysql.com/doc/refman/5.0/en/charset-unicode-sets.html


4
ปรับปรุงประสิทธิภาพเล็กน้อย คุณแน่ใจเกี่ยวกับเรื่องนี้? publib.boulder.ibm.com/infocenter/db2luw/v9r5/index.jsp?topic=/…การเปรียบเทียบที่คุณเลือกอาจส่งผลต่อประสิทธิภาพของการสืบค้นในฐานข้อมูลอย่างมีนัยสำคัญ
Adam Ramadhan

62
นี่สำหรับ DB2 ไม่ใช่ MySQL นอกจากนี้ยังไม่มีตัวเลขหรือเกณฑ์มาตรฐานที่เป็นรูปธรรมดังนั้นคุณเพียงแค่อ้างอิงจากความเห็นของผู้เขียน
Eran Galperin

3
โปรดทราบว่าหากคุณต้องการใช้ฟังก์ชั่นมีข้อผิดพลาดใน MySQL (เวอร์ชันที่เผยแพร่ในปัจจุบันส่วนใหญ่) ที่ฟังก์ชั่นมักจะส่งกลับสตริงโดยใช้ utf8_general_ci ทำให้เกิดปัญหาหากคุณใช้การเปรียบเทียบใหม่สำหรับสตริงของคุณ - ดูbugs.mysql.com/ bug.php? id = 24690
El Yobo

1
จากประสบการณ์ของฉันกับสถานที่ที่แตกต่างกันฉันมักจะใช้utf8_unicode_*
Shiplu Mokaddim

11
อัพเดท: สำหรับรุ่นใหม่แนะนำและutf8mb4 utf8mb4_unicode_520_ciสิ่งเหล่านี้จะให้ส่วนที่เหลือของจีนรวมถึงการปรับปรุงที่ดีขึ้น
Rick James

128

ที่จริงแล้วคุณอาจต้องการที่จะใช้หรือutf8_unicode_ciutf8_general_ci

  • utf8_general_ci เรียงลำดับโดยการกำจัดสำเนียงทั้งหมดและเรียงลำดับราวกับว่าเป็น ASCII
  • utf8_unicode_ci ใช้ลำดับการเรียง Unicode ดังนั้นจึงเรียงลำดับอย่างถูกต้องในภาษาอื่น ๆ

อย่างไรก็ตามหากคุณใช้สิ่งนี้เพื่อจัดเก็บข้อความภาษาอังกฤษเท่านั้นสิ่งเหล่านี้ไม่ควรแตกต่างกัน


1
ฉันชอบคำอธิบายของคุณ! สิ่งที่ดี. แต่ฉันต้องการความเข้าใจที่ดีขึ้นว่าทำไมการเรียงลำดับ Unicode จึงเป็นวิธีที่ดีกว่าในการจัดเรียงอย่างถูกต้อง
การออกแบบ weia

14
@ อดัมมันขึ้นอยู่กับกลุ่มเป้าหมายของคุณ การเรียงลำดับเป็นปัญหาที่ยุ่งยากในการแปลอย่างถูกต้อง เช่นในนอร์เวย์ตัวอักษรÆØÅเป็นตัวอักษร 3 ตัวสุดท้าย ด้วย utf8_general_ci, ØและÅได้รับการแปลงเป็น O และ A ซึ่งทำให้พวกเขาอยู่ในตำแหน่งที่ผิดอย่างสมบูรณ์เมื่อเรียงลำดับ (ฉันไม่แน่ใจว่าวิธีการจัดการÆเนื่องจากเป็นมัดไม่ใช่อักขระที่เน้นเสียง) การเรียงลำดับนี้จะแตกต่างกันในเกือบทุกภาษาเช่นนอร์เวย์และสวีเดนมีคำสั่งซื้อที่แตกต่างกัน (และตัวอักษรที่แตกต่างกันเล็กน้อยซึ่งถือว่าเท่ากัน): ÆØÅเรียงลำดับÅÆØ (ตัวอักษรจริงคือÅÄÖ) Unicode แก้ไขสิ่งนี้
Vegard Larsen

ดังนั้นสิ่งที่ฉันพูดโดยทั่วไปก็คือคุณควรใช้การเรียงลำดับภาษาเฉพาะถ้าคุณทำได้ แต่ในกรณีส่วนใหญ่ที่ไม่สามารถทำได้ดังนั้นให้ไปใช้การเรียงแบบทั่วไปของ Unicode มันจะยังคงแปลกในบางภาษา แต่ถูกต้องกว่า ASCII
Vegard Larsen

3
@Manatax - ด้วยการเปรียบเทียบ utf8_ ใด ๆ ข้อมูลจะถูกจัดเก็บเป็น utf8 การจัดเรียงเป็นเพียงเกี่ยวกับตัวละครที่ถือว่าเท่ากันและวิธีการเรียงลำดับ
frymaster

2
@ frymaster - ไม่จริงตาม: mathiasbynens.be/notes/mysql-utf8mb4 "MySQL utf8 ของ MySQL อนุญาตให้คุณจัดเก็บ 5.88% ของคะแนนโค้ด Unicode ที่เป็นไปได้ทั้งหมด"
ข้อมูล

120

utf8_general_ciจะมากมากตระหนักถึงปัญหานี้ที่สามารถเกิดขึ้นเมื่อใช้

MySQL จะไม่แยกความแตกต่างระหว่างอักขระบางตัวในข้อความสั่งที่เลือกหากใช้การutf8_general_ciเปรียบเทียบ สิ่งนี้สามารถนำไปสู่ข้อผิดพลาดที่น่ารังเกียจมาก - โดยเฉพาะอย่างยิ่งตัวอย่างเช่นที่มีชื่อผู้ใช้ที่เกี่ยวข้อง ปัญหานี้อาจทำให้ผู้ใช้ที่เป็นอันตรายสามารถสร้างชื่อผู้ใช้ที่ตรงกับบัญชีผู้ดูแลทั้งนี้ขึ้นอยู่กับการใช้งานที่ใช้ตารางฐานข้อมูล

ปัญหานี้จะเปิดเผยตัวเองอย่างน้อยที่สุดในช่วงต้นของเวอร์ชัน 5.x - ฉันไม่แน่ใจว่าพฤติกรรมนี้จะเปลี่ยนไปในภายหลัง

ฉันไม่ใช่ DBA แต่เพื่อหลีกเลี่ยงปัญหานี้ฉันจะไปด้วยเสมอutf8-binแทนที่จะใช้ตัวพิมพ์เล็กและใหญ่

สคริปต์ด้านล่างอธิบายปัญหาด้วยตัวอย่าง

-- 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;

36
-1: นี่เป็นการแก้ไขโดยการใช้คีย์เฉพาะกับคอลัมน์ที่เกี่ยวข้อง คุณจะเห็นพฤติกรรมเดียวกันถ้าทั้งสองมีค่าและ'value' 'valUe'จุดรวมของการเปรียบเทียบคือมันมีกฎสำหรับ (เหนือสิ่งอื่นใด) เมื่อสองสตริงถูกพิจารณาว่ามีค่าเท่ากัน
Hammerite

13
นั่นคือปัญหาที่ฉันพยายามอธิบาย - การเปรียบเทียบทำให้สองสิ่งเท่ากันในความเป็นจริงพวกเขาไม่ได้ตั้งใจที่จะเท่ากันเลย (และดังนั้นข้อ จำกัด ที่ไม่เหมือนใครตรงข้ามกับสิ่งที่คุณต้องการบรรลุ)
Guus

18
แต่คุณอธิบายว่ามันเป็น "ปัญหา" และนำไปสู่ ​​"บั๊ก" เมื่อพฤติกรรมเป็นสิ่งที่การเปรียบเทียบนั้นมีจุดมุ่งหมายเพื่อให้บรรลุ คำอธิบายของคุณถูกต้อง แต่เฉพาะในส่วนที่เป็นข้อผิดพลาดในส่วนของ DBA เพื่อเลือกการเปรียบเทียบที่ไม่เหมาะสม
Hammerite

32
สิ่งนี้คือเมื่อคุณป้อนชื่อผู้ใช้สองชื่อที่ได้รับการพิจารณาโดยการเปรียบเทียบจะไม่ได้รับอนุญาตหากคุณตั้งชื่อผู้ใช้ coloumn ให้ไม่ซ้ำกันซึ่งแน่นอนว่าคุณควรทำ!
นักเรียนของ Hogwarts

12
ฉันตอบทั้งคำตอบนี้และความคิดเห็นของ @ Hammerite เพราะทั้งสองคำที่รวมกันช่วยให้ฉันเข้าใจการเปรียบเทียบ
Nacht - Reinstate Monica

86

ที่ดีที่สุดคือการใช้ชุดตัวอักษรที่มีการเปรียบเทียบutf8mb4utf8mb4_unicode_ci

ชุดอักขระutf8รองรับเฉพาะจุดโค้ด UTF-8 เพียงเล็กน้อยเท่านั้นประมาณ 6% ของอักขระที่เป็นไปได้ utf8รองรับ Basic Multilingual Plane (BMP) เท่านั้น มีเครื่องบินอีก 16 ลำ เครื่องบินแต่ละลำมี 65,536 ตัวอักษร utf8mb4รองรับเครื่องบินทั้ง 17 ลำ

MySQL จะตัดทอนอักขระ UTF-8 ขนาด 4 ไบต์ทำให้ข้อมูลเสียหาย

utf8mb4ชุดอักขระเป็นที่รู้จักใน MySQL 5.5.3 บน 2010-03-24

การเปลี่ยนแปลงที่จำเป็นบางอย่างเพื่อใช้ชุดอักขระใหม่นั้นไม่สำคัญ:

  • อาจจำเป็นต้องทำการเปลี่ยนแปลงในอะแดปเตอร์ฐานข้อมูลแอปพลิเคชันของคุณ
  • จะต้องทำการเปลี่ยนแปลงใน my.cnf รวมถึงการตั้งค่าชุดอักขระการเรียงและการสลับ innodb_file_format เป็น Barracuda
  • คำสั่ง SQL CREATE อาจต้องมี: ROW_FORMAT=DYNAMIC
    • จำเป็นต้องใช้ DYNAMIC สำหรับดัชนีใน VARCHAR (192) ขึ้นไป

หมายเหตุ: การเปลี่ยนBarracudaจากAntelopeอาจต้องเริ่มบริการ MySQL ใหม่มากกว่าหนึ่งครั้ง innodb_file_format_maxไม่เปลี่ยนแปลงจนกว่าจะได้รับบริการ MySQL innodb_file_format = barracudaได้รับการเริ่มต้นใหม่ไปที่:

MySQL ใช้Antelopeรูปแบบไฟล์ InnoDB แบบเก่า Barracudaสนับสนุนรูปแบบแถวแบบไดนามิกซึ่งคุณจะต้องถ้าคุณไม่ต้องการที่จะตีข้อผิดพลาด SQL สำหรับการสร้างดัชนีและคีย์หลังจากที่คุณเปลี่ยนเป็นชุดอักขระ:utf8mb4

  • # 1709 - ขนาดคอลัมน์ดัชนีใหญ่เกินไป ขนาดคอลัมน์สูงสุดคือ 767 ไบต์
  • # 1071 - รหัสที่ระบุยาวเกินไป ความยาวสูงสุดของคีย์คือ 767 ไบต์

สถานการณ์จำลองต่อไปนี้ได้รับการทดสอบบน MySQL 5.6.17: ตามค่าเริ่มต้น MySQL ได้รับการกำหนดค่าดังนี้:

SHOW VARIABLES;

innodb_large_prefix = OFF
innodb_file_format = Antelope

หยุดบริการ MySQL ของคุณและเพิ่มตัวเลือกใน my.cnf ที่มีอยู่ของคุณ:

[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

ตัวอย่างคำสั่ง 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;
  • คุณสามารถดูข้อผิดพลาด # 1709 ที่สร้างขึ้นINDEX contact_idx (contact)หากROW_FORMAT=DYNAMICถูกลบออกจากคำสั่ง CREATE

หมายเหตุ: การเปลี่ยนดัชนีเพื่อ จำกัด จำนวนอักขระสูงสุด 128 ตัวบนcontactจะกำจัดข้อกำหนดสำหรับการใช้ Barracuda ด้วยROW_FORMAT=DYNAMIC

INDEX contact_idx (contact(128)),

หมายเหตุ: เมื่อมีข้อความระบุขนาดของเขตข้อมูลVARCHAR(128)นั่นคือไม่ใช่ 128 ไบต์ คุณสามารถใช้มี 128, 4 ไบต์อักขระหรือ 128, 1 ไบต์อักขระ

INSERTคำสั่งนี้ควรมีอักขระ 'poo' ขนาด 4 ไบต์ในแถวที่ 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💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '', '');

คุณสามารถดูจำนวนพื้นที่ที่lastคอลัมน์ใช้:

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
+--------------------+---------------------+

ในอะแดปเตอร์ฐานข้อมูลของคุณคุณอาจต้องการตั้ง charset และ collation สำหรับการเชื่อมต่อของคุณ:

SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_ci'

ใน PHP สิ่งนี้จะถูกตั้งค่าสำหรับ: \PDO::MYSQL_ATTR_INIT_COMMAND

อ้างอิง:



ข้อมูลเพิ่มเติมเกี่ยวกับวิกิพีเดีย: เครื่องบิน Unicode
Jeremy Postlethwaite

6
utf8mb4_unicode_ci ควรเป็นการเปรียบเทียบที่แนะนำสำหรับโครงการใหม่ในปี 2558
Trevor Gehman

7
อัปเดต ... utf8mb4_unicode_520_ciดีกว่า ในอนาคตจะมีutf8mb4_unicode_800_ci(หรืออะไรทำนองนั้น) ตามที่ MySQL จัดการกับมาตรฐาน Unicode
Rick James เมื่อ

46

การเรียงลำดับจะมีผลต่อการเรียงลำดับข้อมูลและเปรียบเทียบสตริงอย่างไร นั่นหมายความว่าคุณควรใช้การเปรียบเทียบที่ผู้ใช้ส่วนใหญ่คาดหวัง

ตัวอย่างจากเอกสารประกอบสำหรับชุดอักขระ Unicode :

utf8_general_ciก็เป็นที่น่าพอใจสำหรับทั้งเยอรมันและฝรั่งเศสยกเว้นว่า 'ß' เท่ากับ 's' และไม่ใช่ 'ss' หากสิ่งนี้ยอมรับได้สำหรับแอปพลิเคชันของคุณคุณควรใช้ utf8_general_ciเพราะมันเร็วกว่า มิฉะนั้นให้ใช้utf8_unicode_ciเพราะแม่นยำกว่า

ดังนั้น - ขึ้นอยู่กับฐานผู้ใช้ที่คาดหวังและจำนวนที่คุณต้องการในการเรียงลำดับที่ถูกต้อง สำหรับฐานผู้ใช้ภาษาอังกฤษutf8_general_ciควรพอเพียงสำหรับภาษาอื่น ๆ เช่นภาษาสวีเดนได้มีการสร้างการเปรียบเทียบพิเศษ


1
ฉันใช้ utf8_general_ci และใช้เวลาสองสามวินาทีในขณะที่การเรียงลำดับและ armscii_general_ci ทำได้เร็วมากเหตุนี้เกิดขึ้นได้อย่างไรคำถามอีกข้อหนึ่งคุณคิดว่าการเปรียบเทียบแบบไหนที่ใช้ในเว็บไซต์เครือข่ายสังคม

22

โดยพื้นฐานแล้วมันขึ้นอยู่กับว่าคุณคิดอย่างไรกับสตริง

ฉันมักจะใช้ utf8_bin เพราะปัญหาที่เน้นโดย Guus ในความคิดของฉันเท่าที่ฐานข้อมูลควรกังวลสตริงยังคงเป็นเพียงสตริง สตริงคือจำนวน UTF-8 อักขระ อักขระมีการนำเสนอแบบไบนารีดังนั้นทำไมจึงจำเป็นต้องรู้ภาษาที่คุณใช้ โดยปกติผู้คนจะสร้างฐานข้อมูลสำหรับระบบที่มีขอบเขตสำหรับไซต์หลายภาษา นี่คือจุดรวมของการใช้ UTF-8 เป็นชุดอักขระ ฉันเป็นคนเจ้าระเบียบนิดหน่อย แต่ฉันคิดว่าข้อผิดพลาดนั้นมีค่ามากกว่าความได้เปรียบเล็กน้อยที่คุณอาจได้รับจากการทำดัชนี กฎใด ๆ ที่เกี่ยวข้องกับภาษาควรทำในระดับที่สูงกว่า DBMS

ใน "คุณค่า" ในหนังสือของฉันไม่ควรเท่ากับหนึ่งล้านปีในการเท่ากับ "valúe"

ถ้าฉันต้องการเก็บฟิลด์ข้อความและค้นหาแบบตัวพิมพ์เล็กและตัวพิมพ์เล็กฉันจะใช้ฟังก์ชันสตริง MYSQL กับฟังก์ชัน PHP เช่น LOWER () และฟังก์ชัน PHP strtolower ()


9
หากการเปรียบเทียบสตริงของสตริงเป็นการเปรียบเทียบที่คุณต้องการแน่นอนว่าคุณควรใช้การเปรียบเทียบไบนารี แต่หากต้องการยกเลิกการเปรียบเทียบทางเลือกว่าเป็น "ความเสี่ยงต่อการเกิดข้อผิดพลาด" หรือเป็นเพียงเพื่อความสะดวกในการจัดทำดัชนีชี้ให้เห็นว่าคุณไม่เข้าใจประเด็นการเปรียบเทียบอย่างสมบูรณ์
Hammerite

13

สำหรับข้อมูลที่เป็นข้อความ UTF-8 คุณควรใช้utf8_general_ciเพราะ ...

  • utf8_bin: เปรียบเทียบสตริงด้วยค่าไบนารีของแต่ละอักขระในสตริง

  • utf8_general_ci: เปรียบเทียบสตริงโดยใช้กฎภาษาทั่วไปและใช้การเปรียบเทียบแบบตัวพิมพ์เล็กและตัวพิมพ์ใหญ่

อาคามันจะทำให้การค้นหาและการจัดทำดัชนีข้อมูลเร็วขึ้น / มีประสิทธิภาพมากขึ้น / มีประโยชน์มากขึ้น


12

คำตอบที่ได้รับการยอมรับแนะนำอย่างชัดเจนโดยใช้ utf8_unicode_ci และสำหรับโครงการใหม่ที่ยอดเยี่ยมฉันต้องการเชื่อมโยงประสบการณ์ที่ตรงกันข้ามของฉันเมื่อไม่นานมานี้ในกรณีที่ช่วยให้ทุกคนประหยัดเวลา

เนื่องจาก utf8_general_ci คือการเรียงหน้าเริ่มต้นสำหรับ Unicode ใน MySQL ถ้าคุณต้องการใช้ utf8_unicode_ci คุณจะต้องระบุมันในหลายๆ ที่

ตัวอย่างเช่นการเชื่อมต่อไคลเอนต์ไม่เพียง แต่จะมีชุดอักขระเริ่มต้น (สมเหตุสมผลกับฉัน) แต่ยังมีการเปรียบเทียบค่าเริ่มต้น (เช่นการเปรียบเทียบจะเริ่มต้นเป็น utf8_general_ci สำหรับ Unicode เสมอ)

หากคุณใช้ utf8_unicode_ci สำหรับฟิลด์ของคุณสคริปต์ของคุณที่เชื่อมต่อกับฐานข้อมูลจะต้องได้รับการอัปเดตเพื่อกล่าวถึงการจัดเรียงที่ต้องการอย่างชัดเจนมิฉะนั้นการสืบค้นที่ใช้สตริงข้อความอาจล้มเหลวเมื่อการเชื่อมต่อของคุณใช้การเปรียบเทียบเริ่มต้น

ผลที่สุดคือเมื่อทำการแปลงระบบที่มีอยู่ทุกขนาดให้เป็น Unicode / utf8 คุณอาจถูกบังคับให้ใช้ utf8_general_ci เนื่องจากวิธีที่ MySQL จัดการกับค่าเริ่มต้น


8

สำหรับกรณีที่ไฮไลต์โดย Guus ฉันขอแนะนำอย่างยิ่งให้ใช้ utf8_unicode_cs (ตัวพิมพ์เล็กและตัวพิมพ์ใหญ่การจับคู่ที่เข้มงวดการสั่งซื้ออย่างถูกต้องสำหรับส่วนใหญ่) แทน utf8_bin (การจับคู่ที่เข้มงวดการสั่งที่ไม่ถูกต้อง)

หากฟิลด์นั้นมีวัตถุประสงค์ที่จะค้นหาแทนที่จะตรงกับผู้ใช้ให้ใช้ utf8_general_ci หรือ utf8_unicode_ci ทั้งสองตัวพิมพ์เล็กและตัวพิมพ์ใหญ่หนึ่งตัวจะไม่ตรงกัน ('ß' เท่ากับ 's' และไม่ใช่ 'ss') นอกจากนี้ยังมีรุ่นภาษาเฉพาะเช่น utf8_german_ci ซึ่งการจับคู่การสูญเสียเหมาะกว่าสำหรับภาษาที่ระบุ

[แก้ไข - เกือบ 6 ปีต่อมา]

ฉันไม่แนะนำชุดอักขระ "utf8" บน MySQL และแนะนำชุดอักขระ "utf8mb4" แทน ตรงกับเกือบทั้งหมด แต่อนุญาตให้มีอักขระ Unicode น้อยมาก (มาก)

ในความเป็นจริง MySQL ควรมีการปรับปรุงชุดอักขระ "utf8" และการเรียงตามลำดับเพื่อให้ตรงกับข้อกำหนด "utf8" แต่แทนที่จะเป็นชุดอักขระแยกต่างหากและการเรียงตามลำดับเพื่อไม่ส่งผลกระทบต่อการกำหนดหน่วยเก็บข้อมูล .


5
FYI: utf8_unicode_csไม่มีอยู่ utf8 ที่คำนึงถึงขนาดตัวพิมพ์utf8_binเท่านั้น ปัญหาในutf8_binการเรียงลำดับไม่ถูกต้อง ดู: stackoverflow.com/questions/15218077/…
Costa

1
ขอบคุณสำหรับการอัปเดต!
โพร

5

ฉันพบว่าแผนภูมิการเปรียบเทียบเหล่านี้มีประโยชน์ http://collation-charts.org/mysql60/ ฉันไม่แน่ใจว่าจะใช้ utf8_general_ci อย่างไร

ตัวอย่างเช่นที่นี่เป็นแผนภูมิสำหรับ utf8_swedish_ci มันแสดงให้เห็นว่าตัวละครที่มันตีความเหมือนกัน http://collation-charts.org/mysql60/mysql604.utf8_swedish_ci.html


รสชาติที่แตกต่างของแผนภูมิ: mysql.rjweb.org/utf8_collations.html
Rick James

2

ในไฟล์อัพโหลดฐานข้อมูลของคุณเพิ่มบรรทัด followin ก่อนบรรทัดใด ๆ :

SET NAMES utf8;

และปัญหาของคุณควรได้รับการแก้ไข


2
อ่านคำถาม: ในอดีตที่ผ่านมาฉันได้ตั้งค่า PHP ให้แสดงผลใน "UTF-8" แต่การเปรียบเทียบใดที่เปรียบเทียบใน MySQL? ฉันคิดว่ามันเป็นหนึ่งใน UTF-8 แต่ฉันเคยใช้ utf8_unicode_ci, utf8_general_ci และ utf8_bin มาก่อน
Jitesh Sojitra

5
คำตอบนี้ไม่เกี่ยวข้องกับคำถาม นอกจากนี้การออกSET NAMESแบบสอบถามโดยตรงจะไม่แจ้งให้ลูกค้าทราบเกี่ยวกับการเข้ารหัสและอาจทำให้คุณสมบัติบางอย่างเช่นคำสั่งที่เตรียมไว้มีความละเอียดอ่อนมาก
ÁlvaroGonzález
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.