VBA与SQL Server排序不一致:匹配排序规则及字符比较异常排查
问题描述
我从采用Hebrew_CI_AS排序规则的SQL Server中获取包含希伯来文、英文及通配符的单列数组,数组有时呈升序排列,有时则不是。我在VBA中编写了IsArrSortedAsc函数用于判断数组是否升序,但即使SQL语句添加了ORDER BY ASC子句,该函数仍始终返回FALSE,怀疑是SQL与VBA的排序规则不匹配所致。想知道SQL中需使用哪种排序规则才能匹配VBA的排序逻辑。
此外,测试发现VBA的StrComp函数存在字符比较异常:当STR1="א'"、STR2="'א"时,StrComp(STR1, STR2, vbTextCompare)返回1(表示STR1大于STR2),但按希伯来文从右到左的视觉逻辑,STR1应该更小,寻求解决思路。
相关VBA代码
Sub MySub() Dim ArrayThatReturnsFromSQL As Variant Dim IsArraySorted As Boolean ' 单列数组 ArrayThatReturnsFromSQL = ThisFunctionReturnsArrayFromSQL IsArraySorted = IsArrSortedAsc(ArrayThatReturnsFromSQL) End Sub Public Function IsArrSortedAsc(Arr As Variant) As Boolean Dim aRw As Long Dim Ub As Long Dim tmpBool As Boolean Ub = UBound(Arr) tmpBool = True If Ub > 0 Then For aRw = 1 To Ub If Arr(aRw) < Arr(aRw - 1) Then tmpBool = False Exit For End If Next End If IsArrSortedAsc = tmpBool End Function
测试代码
Sub Test() Dim STR1 As String Dim STR2 As String ' 备注:希伯来文从右到左书写 ' 按视觉逻辑STR1应该小于STR2 STR1 = "א'" STR2 = "'א" ' 返回1,表示STR1大于STR2,与预期不符 Debug.Print StrComp(STR1, STR2, vbTextCompare) End Sub
解决方案
一、SQL排序规则匹配VBA逻辑
VBA中直接使用<或>进行字符串比较时,采用的是二进制比较(vbBinaryCompare),依赖字符的Unicode编码值;而StrComp指定vbTextCompare时,依赖系统区域设置的排序规则。要让SQL的排序结果与VBA判断一致,需分两种情况处理:
- 匹配VBA的二进制比较逻辑
如果你的IsArrSortedAsc函数使用默认的<比较(即二进制比较),SQL需使用二进制排序规则,确保按字符的Unicode编码值排序。推荐使用Hebrew_BIN2(比旧的Hebrew_BIN更符合Unicode标准):
SELECT YourColumn FROM YourTable ORDER BY YourColumn COLLATE Hebrew_BIN2 ASC;
- 匹配VBA的文本比较逻辑
如果后续要改用StrComp(..., vbTextCompare)来判断排序,SQL需使用与Windows系统兼容的希伯来文排序规则,即Hebrew_100_CI_AS(SQL Server中对应Windows排序的版本,区别于传统的Hebrew_CI_AS):
SELECT YourColumn FROM YourTable ORDER BY YourColumn COLLATE Hebrew_100_CI_AS ASC;
二、VBA StrComp函数异常的解决思路
明确排序逻辑的本质
VBA的vbTextCompare是按系统区域设置的排序权重比较,而非视觉上的从右到左顺序。STR1="א'"的逻辑存储顺序是א(U+05D0)在前、'(U+0027)在后,STR2="'א"是'在前、א在后。按Unicode排序权重,א的权重高于',所以StrComp返回1是符合文本排序规则的,并非异常。按视觉顺序比较的处理方法
如果需要按希伯来文的视觉显示顺序(从右到左)比较,需先将字符串的逻辑顺序转换为视觉顺序。以下是简单的反转字符实现(仅适用于纯右左文本+左右字符的简单场景):
Function GetVisualOrder(str As String) As String Dim charArr() As String Dim i As Integer ReDim charArr(Len(str) - 1) For i = 1 To Len(str) charArr(Len(str) - i) = Mid(str, i, 1) Next i GetVisualOrder = Join(charArr, "") End Function
使用转换后的字符串进行比较:
Debug.Print StrComp(GetVisualOrder(STR1), GetVisualOrder(STR2), vbTextCompare)
复杂的双向文本(混合左/右方向字符)需调用Windows API(如GetCharacterPlacement)来获取准确的视觉顺序。
- 确保系统区域设置正确
如果系统区域设置为非希伯来文,vbTextCompare会使用当前区域的排序规则,导致希伯来文比较结果不符合预期。需将系统区域设置为希伯来文,或在VBA中通过API强制指定排序规则。
内容的提问来源于stack exchange,提问作者xl0911

