Mysql

Varchar 上的 MySQL 區分大小寫連接

  • June 3, 2019

我有兩個表,它們都具有相同的數據類型、字元集和明確指定的排序規則。

CLERK CHAR(3) CHARACTER SET latin1 COLLATE latin1_bin NULL

對於欄位上區分大小寫的聯接CLERK,我是否還需要在聯接子句中指定排序規則,或者它在 DDL 級別指定的事實是否意味著聯接中不需要它?

FROM
   CUSTOMER S JOIN 
   CLERK C ON S.CLERK = C.CLERK COLLATE latin1_bin

對於欄位上區分大小寫的聯接CLERK,我是否還需要在聯接子句中指定排序規則,或者它在 DDL 級別指定的事實是否意味著聯接中不需要它?

**簡單的回答:無需在查詢中指定排序規則。**將使用 DDL 級別的排序規則(尤其是當它在兩邊都相同並且兩邊都是列時)(這就是我們首先在 DDL 級別指定它的原因)。


**詳細答案:**在給定操作(連接、謂詞等)中使用的排序規則有一個優先級層次結構。使用的排序規則不僅取決於它是列還是文字等,還取決於它是 Unicode 還是非 Unicode,甚至是二進制還是非二進制。完整的描述可以在這裡找到,Collat​​ion Coercibility in Expressions,這是 MySQL 5.7 文件,因為您使用的是該版本。一個相當有趣的規則是:

對於操作數來自相同字元集但混合了_bin排序規則和_ci排序_cs規則的操作,使用_bin排序規則。這類似於混合非二進制和二進製字元串的操作如何將操作數評估為二進製字元串,除了它用於排序規則而不是數據類型。

綜上所述,我需要指出“二進制排序規則區分大小寫”的說法雖然非常普遍,但絕對是不正確的。

  1. 在一個非常基本的層面上,排序是不同的。區分大小寫的排序規則會將“a”與“A”排序(儘管哪個先出現可能取決於文化)、“b”與“B”等等。二進制排序規則將根據每個字元的基礎值/程式碼點進行排序,當一個字元的大寫和小寫版本根據它們的值被其他字元分隔時,這一點變得非常明顯。
  2. 大小寫“敏感”意味著您也可以對字元的其他屬性“不敏感”,主要是重音。這也是非 Unicode 字元集的唯一其他屬性,但 Unicode 允許對假名類型敏感、對寬度敏感,而 SQL Server(截至 2017 版)甚至允許對變體選擇器敏感。二進制排序規則不允許一個字元等於它自身以外的任何東西,即使它存在其他形式。同樣,這在非 Unicode 字元集中並不會真正發生,但在 Unicode 中,一個字元可以有多個版本,包括寬、上標、下標、斜體、顛倒(稱為“轉身”)等. MySQL,從 8.0 版開始(據我所知),ut8mb4字元集和排序規則):

為了說明這兩點,我在很棒的db<>fiddle上設置了一個展示。我不得不使用 MySQL 8.0 不僅獲得_ai_ci排序規則,而且因為COLLATE latin1_general_ci子句(倒數第二個查詢,#5)沒有影響(出於某種奇怪的原因;文件指出,當排序規則名稱只有_ci, then_ai是隱含的,然而對於db<>fiddle上的 MySQL 5.6 和 5.7,行為仍然是_as_csor _bin)。

並且,甚至還有其他方式使二進制排序規則不“區分大小寫”。他們無法解釋:

  • 結合變音符號
  • 擴展
  • 宮縮

我之前沒有列出或提供這些範例,因為它們不屬於 8 位編碼,latin1而是 8 位編碼。這些是 Unicode 的特性,因此應該適用於任何 Unicode 排序規則(雖然我沒有在 MySQL 上測試它們,但它們在 SQL Server 中正確實現)。

PS 我在下面的文章中詳細解釋了所有這些(包括上面直接提到的 Unicode 特定行為):不,二進制排序規則不區分大小寫。這目前僅在 SQL Server 的上下文中建構,但是當我有時間時,我可以在我為這個答案提出的範例中工作。重要的是這個概念在 RDBMS 中是相同的。


作為參考(以防db<>fiddle無法訪問),查詢是:

查詢 1

CREATE TABLE CLERK (
 CLERK_ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
 CLERK CHAR(3) CHARACTER SET latin1 COLLATE latin1_bin NULL
);

CREATE TABLE CUSTOMER (
 CUSTOMER_ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
 NAME VARCHAR(10) NOT NULL,
 CLERK CHAR(3) CHARACTER SET latin1 COLLATE latin1_bin NULL,
 CLERK_CI_AI CHAR(3) CHARACTER SET latin1 COLLATE latin1_general_ci NULL
);

INSERT INTO CUSTOMER (NAME, CLERK, CLERK_CI_AI) VALUES('John Doe', 'Ü', 'Ü');

INSERT INTO CLERK (CLERK) VALUES('Ü');
INSERT INTO CLERK (CLERK) VALUES('ü');
INSERT INTO CLERK (CLERK) VALUES('U');
INSERT INTO CLERK (CLERK) VALUES('u');
INSERT INTO CLERK (CLERK) VALUES('Ù');
INSERT INTO CLERK (CLERK) VALUES('ù');
INSERT INTO CLERK (CLERK) VALUES('Û');
INSERT INTO CLERK (CLERK) VALUES('û');

INSERT INTO CLERK (CLERK) VALUES('Y');
INSERT INTO CLERK (CLERK) VALUES('y');
INSERT INTO CLERK (CLERK) VALUES('Ý');
INSERT INTO CLERK (CLERK) VALUES('ý');

查詢 2

SELECT 0 AS "ver", C.*
FROM   CLERK C;

查詢 3

SELECT "bin sort" AS "ver", C.*
FROM   CLERK C
ORDER BY C.CLERK;

查詢 4

SELECT "cs sort" AS "ver", C.*
FROM   CLERK C
ORDER BY C.CLERK COLLATE latin1_general_cs;

查詢 5

SELECT 1 AS "ver", S.*, C.*
FROM CUSTOMER S
JOIN CLERK C
 ON S.CLERK = C.CLERK;

查詢 6

SELECT 2 AS "ver", S.*, C.*
FROM CUSTOMER S
JOIN CLERK C
 ON S.CLERK = C.CLERK COLLATE latin1_bin;

查詢 7

SELECT 3 AS "ver", S.*, C.*
FROM CUSTOMER S
JOIN CLERK C
 ON S.CLERK_CI_AI = C.CLERK;

查詢 8

SELECT 4 AS "ver", S.*, C.*
FROM CUSTOMER S
JOIN CLERK C
 ON S.CLERK_CI_AI = C.CLERK COLLATE latin1_bin;

查詢 9

# is accent-sensitive even though documentation states it should be
# accent-INsensitive
# 
SELECT 5 AS "ver", S.*, C.*
FROM CUSTOMER S
JOIN CLERK C
 ON S.CLERK_CI_AI = C.CLERK COLLATE latin1_general_ci;

查詢 10

SELECT 6 AS "ver", S.*, C.*
FROM CUSTOMER S
JOIN CLERK C
 ON CONVERT(S.CLERK_CI_AI using utf8mb4)
      = CONVERT(C.CLERK USING utf8mb4) COLLATE utf8mb4_0900_ai_ci;

引用自:https://dba.stackexchange.com/questions/239539