根据另一个范围的列值序列对范围列进行排序

Sort a Range column based on the sequence of column values from another Range

我在工作表中有一个范围,如下所示,我想在另一个工作表范围列 Status:

Beta 列上自定义排序
+---------+----------+-----+
| Alpha   | Beta     | Gama|
+---------+----------+-----+
| PROJ 1  | COMPLETE | 245 |
+---------+----------+-----+
| PROJ 2  | PENDING  | 344 |
+---------+----------+-----+
| PROJ 3  | COMPLETE | 122 |
+---------+----------+-----+
| PROJ 4  | COMPLETE | 111 |
+---------+----------+-----+
| PROJ 5  | PENDING  | 101 |
+---------+----------+-----+
| PROJ 6  | PENDING  | 222 |
+---------+----------+-----+
| PROJ 7  | PROGRESS | 343 |
+---------+----------+-----+
| PROJ 8  | PROGRESS | 256 |
+---------+----------+-----+
| PROJ 9  | PROGRESS | 606 |
+---------+----------+-----+
| PROJ 10 | COMPLETE | 234 |
+---------+----------+-----+

像这样:

+---------+----------+---------+
| Alpha   | Beta     | Gama    |
+---------+----------+---------+
| PROJ 7  | PROGRESS | 343     |
+---------+----------+---------+
| PROJ 8  | PROGRESS | 256     |
+---------+----------+---------+
| PROJ 9  | PROGRESS | 606     |
+---------+----------+---------+
| PROJ 2  | PENDING  | 344     |
+---------+----------+---------+
| PROJ 5  | PENDING  | 101     |
+---------+----------+---------+
| PROJ 6  | PENDING  | 222     |
+---------+----------+---------+
| PROJ 1  | COMPLETE | 245     |
+---------+----------+---------+
| PROJ 3  | COMPLETE | 122     |
+---------+----------+---------+
| PROJ 4  | COMPLETE | 111     |
+---------+----------+---------+
| PROJ 10 | COMPLETE | 234     |
+---------+----------+---------+

基于另一个范围列的唯一值:

+----------+
| STATUS   |
+----------+
| PROGRESS |
+----------+
| PENDING  |
+----------+
| COMPLETE |
+----------+

是否可以在 VBA 中使用自定义排序功能?例如像下面这样的东西(不工作):

Sub SortTable()

Dim rng1 As Range, rng2 As Range
Dim ws1 As Worksheet, ws2 As Worksheet

Set ws1 = ThisWorkbook.Worksheets("Sheet1")
Set ws2 = ThisWorkbook.Worksheets("Sheet2")

With ws1
  Set rng1 = .Range(.Cells(1, 1), .Cells(11, 3))
End With

With ws2
  Set rng2 = .Range(.Cells(1, 1), .Cells(4, 3))
End With
  
With rng1.Sort
    .SortFields.Add Key:=rng2.ListColumns("Status").Range, Order:=xlAscending
    .Header = xlYes
    .Apply
End With

End Sub

请测试下一个代码。它假定 条件范围位于“Sheet2”工作表的“A1:A4”中。该代码将丢弃从“E2”开始的处理结果。如果您喜欢它的 return,请修改 ws1.Range("A2") 中的最后一行代码。它将覆盖现有数据:

Sub SortTable()
 Dim ws1 As Worksheet, ws2 As Worksheet, i As Long, j As Long, k As Long
 Dim Dim rngC As Range, arrL, arr, arrFin, mtch, dict As Object, arrL, arr, arrFin, mtch, dict As Object

 Set ws1 = ThisWorkbook.Worksheets("Sheet1")
 Set ws2 = ThisWorkbook.Worksheets("Sheet2")

 With ws1
    arrL = .Range(.cells(2, 1), .cells(11, 3)).value 'place the range in an array for faster iteration
 End With

 With ws2
     Set rngC = .Range(.cells(2, 1), .cells(4, 1))
 End With

 ReDim arrFin(1 To UBound(arrL), 1 To UBound(arrL, 2)) 'redim the final array
 Set dict = CreateObject("scripting.Dictionary")
 For i = 1 To UBound(arrL) 'create unique keys in the dictionary and add the necessary information separated by "|" and "::"
     dict(arrL(i, 2)) = dict(arrL(i, 2)) & "|" & arrL(i, 1) & "::" & arrL(i, 3)
 Next i

 For i = 1 To rngC.rows.count
    mtch = Application.match(rngC(i, 1).value, dict.Keys, 0)
    arrL = Split(Mid(dict.items()(mtch - 1), 2), "|")
    For j = 0 To UBound(arrL)
        arr = Split(arrL(j), "::")
        k = k + 1
        arrFin(k, 1) = arr(0): arrFin(k, 2) = dict.Keys()(mtch - 1): arrFin(k, 3) = arr(1)
    Next
 Next i
 'drop the processed array content at once:
 ws1.Range("E2").Resize(UBound(arrFin), UBound(arrFin, 2)).value = arrFin
End Sub

已编辑以证明 > 5000 行没有匹配限制

请复制并运行下一个代码:

Sub testMatchLimitations()
 Dim sh As Worksheet, mtch, arr
 Set sh = ActiveSheet
 sh.Range("C1:C2").value = Application.Transpose(Array("AAA1", "AAA2"))
 
 sh.Range("C1:C2").AutoFill Destination:=sh.Range("C1:C" & sh.rows.count), Type:=xlFillDefault
 arr = sh.Range("C1:C" & sh.rows.count).value
 mtch = Application.match("AAA1048566", arr, 0)
  Debug.Print mtch
End Sub

它将引发 最大 Excel 行数没有错误...

第二次编辑:

第二个(更简单的)版本,添加一个辅助列并根据它排序:

Sub SortTable_1()
 Dim ws1 As Worksheet, ws2 As Worksheet, i As Long
 Dim rngC As Range, lastR As Long, lastCol As Long, arrL, arrFin, mtch

 Set ws1 = ActiveSheet ' ThisWorkbook.Worksheets("Sheet1")
 Set ws2 = ws1.Next ' ThisWorkbook.Worksheets("Sheet2")
 lastR = ws1.Range("A" & ws1.rows.count).End(xlUp).row
 lastCol = 4 'it can be calculated...
 With ws1
    arrL = .Range(.cells(2, 1), .cells(lastR, 3)).value
 End With

 With ws2
    Set rngC = .Range(.cells(2, 1), .cells(4, 1))
 End With
 ReDim arrFin(1 To UBound(arrL), 1 To 1)
 For i = 1 To UBound(arrL)
    mtch = Application.match(arrL(i, 2), rngC, 0)
    If Not IsError(mtch) Then
        arrFin(i, 1) = mtch
    Else
        MsgBox "There is no match in the criteria range for value in C" & i + 1 & "(" & arrL(i, 2) & "...": Exit Sub
    End If
 Next i
 
 ws1.cells(1, lastCol).value = "Rank"
 ws1.cells(2, lastCol).Resize(UBound(arrFin), 1).value = arrFin
 ws1.Range("A1", ws1.cells(lastR, lastCol)).Sort key1:=ws1.Range("D1"), Order1:=xlAscending, Header:=xlYes
 ws1.cells(1, lastCol).EntireColumn.Delete
End Sub

如何使用sql。结果在新的 sheet Sheet3.

Sub DoSQL(Ws As Worksheet, strSQL As String)
 
    Dim Rs As Object
    Dim strConn As String
    Dim i As Integer
 
    strConn = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
        "Data Source=" & ThisWorkbook.FullName & ";" & _
            "Extended Properties=Excel 12.0;"
 
 
    Set Rs = CreateObject("ADODB.Recordset")
    Rs.Open strSQL, strConn
 
    If Not Rs.EOF Then
         With Ws
            .Range("a1").CurrentRegion.ClearContents
            For i = 0 To Rs.Fields.Count - 1
               .Cells(1, i + 1).Value = Rs.Fields(i).Name
            Next
            .Range("a" & 2).CopyFromRecordset Rs
        End With
    End If
    Rs.Close
    Set Rs = Nothing
End Sub
Sub test()
    Dim strSQL As String
    Dim Ws As Worksheet
    Dim Ws1 As Worksheet
    Dim vDB, vS
    Dim i As Long
    
    Set Ws1 = Sheets("Sheet1")
    With Ws1
        vDB = .Range("a2", .Range("a" & Rows.Count).End(xlUp))
    End With
    For i = 1 To UBound(vDB, 1)
        vS = Split(vDB(i, 1))
        vDB(i, 1) = vS(0) & " " & Format(Val(vS(1)), "000#")
    Next i
    With Ws1
        .Range("d1") = "Alpha2"
        .Range("d2").Resize(UBound(vDB, 1), UBound(vDB, 2)) = vDB
    End With
    
    Set Ws = Sheets("Sheet3")
    
    strSQL = "select a.Alpha, Beta, Gama "
    strSQL = strSQL & "FROM "
    strSQL = strSQL & "( select * from [Sheet1$]  , [Sheet2$] as b "
    strSQL = strSQL & "where beta = b.status ) as a "
    strSQL = strSQL & "ORDER BY b.status desc, a.Alpha2 "

    DoSQL Ws, strSQL
End Sub

Sheet1 图片

新建字段Alpah2。

Sheet3 图片

这是我使用 CustomLists.

得出的一个更简单的解决方案的摘录
Sub SortRange()
.....

'Custom Sort `Rng1`
xLastColumn = .Range("1:1").Cells(.Columns.Count).End(xlToLeft).Column
xLastRow = .Cells.SpecialCells(xlCellTypeLastCell).Row

KeyValues = wk3.Cells.Range("F46:F54").Value2 ' `Rng2
n = Application.GetCustomListNum(KeyValues)
Application.DeleteCustomList n
            
Application.AddCustomList listArray:=KeyValues
sortNumber = Application.CustomListCount
            
wk2.Sort.SortFields.Clear
            
wk2.Sort.SortFields.Add _
  Key:=wk2.Range(yCell.Offset(1, 0), wk2.Cells(xLastRow, yCell.Column)), _
  SortOn:=xlSortOnValues, _
  Order:=xlAscending, _
  CustomOrder:=sortNumber, _
  DataOption:=xlSortNormal
          
  With wk2.Sort
    .SetRange wk2.Range(wk2.Cells(1, 1), wk2.Cells(xLastRow, xLastColumn))
    .Header = xlYes
    .MatchCase = False
    .Orientation = xlTopToBottom
    .SortMethod = xlPinYin
    .Apply
  End With
End With

.....

End Sub