SQL Server 2008 R2日语(NVARCHAR)字符串比较出错?

时间:2017-10-05 21:15:52

标签: sql-server unicode sql-server-2008-r2

问题

当我在SQL Server 2008 R2上运行以下查询时,两个不同的日语unicode字符串被视为相等:

SELECT
    CASE
        WHEN N'食料' = N'食料ㇰ ㇱ ㇲ ㇳ'
            THEN 1
        ELSE 0
    END;

--result: 1

我知道汉字后面的假名是半宽的,但由于没有类似的全宽假名,我不会指望宽度敏感度或假名敏感度。但是,如果将假名替换为全宽版本,则比较将按预期运行:

SELECT
    CASE
        WHEN N'食料' = N'食料ク シ ス ト'
            THEN 1
        ELSE 0
    END;

--result: 0

尝试解决方案

这让我觉得这个问题可能与我的整理有关,即SQL_Latin1_General_CP1_CI_AS

首先,我尝试Latin1_General_CI_AS,以防它是SQL Unicode比较的怪癖,但这并没有解决问题。

然后,我想我会尽可能使用最严格的整理(所有敏感度),但其他整理包括Latin1_General_CS_AS_KS_WSJapanese_Unicode_CS_AS_KS_WS在使用半宽尾随假名时没有改变结果(所有正确确定了与全宽尾随假名的差异。)

要验证字符串级别的字符串是否不同,我在删除字符串的N(nvarchar)指定后运行半宽跟踪假名查询并验证它返回{{的预期结果1}}。

问题

这里发生了什么?我只是不尝试正确的整理?这是SQL Server 2008 R2中的错误吗?我不知道有关于日语Unicode的具体内容吗?为什么半宽尾随假名的存在不会使这些字符串不同?

PS我不懂日语,所以如果我搞砸了我道歉的人物描述。

1 个答案:

答案 0 :(得分:1)

它的长短在于,在大量的排序规则中,第一个例子中的字符等于空格。

在进行字符串比较时,SQL Server会删除字符串末尾的尾随空格(当您使用var isProductionEnv = process.env.NODE_ENV === 'production'; app.use([ cookieParser(process.env.SESSION_SECRET), session({ resave: false, saveUninitialized: true, secret: process.env.SESSION_SECRET, store: new MongoStore({ url: MONGO_URL, autoReconnect: true }), proxy: isProductionEnv, cookie: { secure:isPrudictionEnv, }, }), lusca({ csrf: true, xframe: 'SAMEORIGIN', xssProtection: true, })]); app.set('trust proxy', isProductionEnv); 时会有一个例外,但您在此处没有这样做)。

因此,例如,在字符串LIKE中,N'食料ㇰ ㇱ ㇲ ㇳ'之后的每个字符都被视为尾随空格,并在进行字符串比较时被删除。

要使用给定的排序规则进行快速检查,您可以运行以下查询:

做一些快速测试......   - 不会将这些特定字符视为空格的日语归类:任何BIN归类,Japanese_Bushu_Kakusu,Japanese_XJIS   - 日语归类 将这些特定字符视为空格:日语,日语90,日语_Unicode

注意:Japanese_Unicode_CS_AS_KS_WS中有超过21000个字符被视为空格。您可以通过针对给定的排序规则运行以下查询来检查此问题:

WITH 
    Vals AS (SELECT FullString, StringNum FROM (VALUES (N'食料', 1), (N'食料ㇰ ㇱ ㇲ ㇳ', 2), (N'食料ク シ ス ト', 3)) AS T(FullString, StringNum)),
    CTE AS -- A recursive CTE to split the characters up in your strings and check the individual characters. 
    (
        SELECT FullString,
                StringNum,
                IndividualCharacter = SUBSTRING(FullString, 1, 1),
                UnicodeNumber = UNICODE(SUBSTRING(FullString, 1, 1)),
                UnicodeBinary = CAST(SUBSTRING(FullString, 1, 1) AS VARBINARY(2)),
                CharPosition = 1
        FROM Vals
        UNION ALL 
        SELECT V.FullString,
                V.StringNum,
                IndividualCharacter = SUBSTRING(V.FullString, C.CharPosition + 1, 1),
                UnicodeNumber = UNICODE(SUBSTRING(V.FullString, C.CharPosition + 1, 1)),
                UnicodeBinary = CAST(SUBSTRING(V.FullString, C.CharPosition + 1, 1) AS VARBINARY(2)),
                CharPosition = C.CharPosition + 1
        FROM Vals AS V
        JOIN CTE AS C
            ON C.StringNum = V.StringNum
        WHERE C.CharPosition + 1 <= LEN(V.FullString)
    )
SELECT C.*,
       CharacterEqualToSpace = CASE WHEN NCHAR(C.UnicodeNumber) COLLATE Japanese_Unicode_CS_AS_KS_WS = NCHAR(32) THEN 1 ELSE 0 END,
       FullStringWithoutSpace = SUBSTRING(C.FullString, 1, (SELECT MAX(CharPosition) FROM CTE AS C2 WHERE C2.StringNum = C.StringNum AND NCHAR(C2.UnicodeNumber) COLLATE Japanese_Unicode_CS_AS_KS_WS != NCHAR(32))) -- Eliminate white space on the end for this collation, with a substring ending at the last character that does not equal white space.
FROM CTE AS C
ORDER BY StringNum, CharPosition;