2016-03-30 2 views
1

Описание проблемы: при объединении двух таблиц с использованием Excel и ADO (ACE OLEDB 12.0) в алфавитно-цифровых строках ADO не различает клавиши «a12a», и «A12a» (они рассматривают их так, как если бы они были одинаковыми, т.е. без учета регистра). У меня, однако, есть буквенно-цифровые клавиши в моих данных. Соединение свяжет данные неправильно!ADO (Excel, ACE OLEDB 12.0) не сравнивается с ключевыми строками, чувствительными к регистру

Я построил небольшой пример в книге Excel, чтобы воспроизвести поведение. в программе Excel Учебное пособие содержит 3 листов:

  1. AlphaNum1
  2. AlphaNum2
  3. Результат

AlphaNum1 Лист содержит следующие данные

Key Val 
a12b 1 
A12b 2 
a12B 3 
A12B 4 
e12f 7 
E12F 8 
1234 9 

AlphaNum2 лист содержит следующие данные:

Key Val 
a12b 1 
A12b 2 
a12B 3 
A12B 4 
c12d 5 
C12D 6 
1234 9 

Я использую следующий код VBA для подключения к ADO и объединения таблиц (LEFT JOIN):

Sub AlphaNumTest() 
    Dim oAdoConnection As New ADODB.Connection 
    Dim oAdoRecordset As New ADODB.Recordset 
    Dim sAdoConnectString As String, sPfad As String 
    Dim sQuery As String 
    On Error GoTo ExceptionHandling 
    sPfad = ThisWorkbook.FullName 
    sAdoConnectString = "Provider=Microsoft.ACE.OLEDB.12.0; Extended Properties='Excel 12.0 Xml;HDR=YES;';Data Source=" & sPfad 

    oAdoConnection.Open sAdoConnectString 
    sQuery = "Select a1.[Key], a2.[Val] from [AlphaNum1$] a1 LEFT JOIN [AlphaNum2$] a2 ON a1.[Key] = a2.[Key]" 
    With oAdoRecordset 
     .Source = sQuery 
     .ActiveConnection = oAdoConnection 
     .Open 
    End With 

    Dim writeRange As Range 
    Dim headerRange As Range 

    'Set headerRange = ThisWorkbook.Sheets("WriteHere").Range("A1") 
    Set writeRange = ThisWorkbook.Sheets("Result").Range("A2") 

    ' print the table header from recordset 
    For i = 0 To oAdoRecordset.Fields.Count - 1 
     ' careful! the recordset is zero-indexed like it should be! Excel table however starts at index one, thus the i+1~ 
     ThisWorkbook.Sheets("Result").Cells(1, i + 1).Value = oAdoRecordset.Fields(i).Name 
     ' set bold 
     ThisWorkbook.Sheets("Result").Cells(1, i + 1).Font.Bold = True 
    Next i 

    ' print the data directly from recordset! 
    writeRange.CopyFromRecordset oAdoRecordset 


CleanUp: 
    On Error Resume Next ' Lazy skip 
    oAdoRecordset.Close 
    oAdoConnection.Close 
    Set oAdoRecordset = Nothing 
    Set oAdoConnection = Nothing 
    Exit Sub 
ExceptionHandling: 
    MsgBox "Fehler: " & Err.Description 
    Resume CleanUp 
End Sub 

Обратите внимание, что это не имеет значения, если я использую внутренний или LEFT JOIN; результат неправильный, а в этом примере я использую LEFT JOIN для демонстрации поведения.

Результат результата: AlphaNum1.Key и AlphaNum2.Val, соединенный LEFT JOIN.

Ожидаемый результат (Я присоединяюсь с помощью "=" не НРАВИТСЯ ...) является:

Key Val 
a12b 1 
A12b 2 
a12B 3 
A12B 4 
e12f  
E12F  
1234 9 

Но ADO дает мне Фактический результат (он рассматривает случай Ключи нечувствительным .. .):

Key Val 
a12b 4 
a12b 3 
a12b 2 
a12b 1 
A12b 4 
A12b 3 
A12b 2 
A12b 1 
a12B 4 
a12B 3 
a12B 2 
a12B 1 
A12B 4 
A12B 3 
A12B 2 
A12B 1 
e12f  
E12F  
1234 9 

Любые идеи, почему ADO ведет себя, как это? Любые идеи о том, как/если я могу изменить поведение?

+0

Это уже было задано и ответили [здесь] (http://stackoverflow.com/questions/29533346/join-2-tables-case-sensitive-upper-and-lower-case) – leowyn

+0

Спасибо для ответа - но как решить проблему с помощью ADO? Когда я добавляю сортировку (используя COLLATE latin1_bin), я получаю сообщение об ошибке при открытии RecordSet. Используя BINARY, я получаю синтаксическую ошибку. Есть ли ключевое слово в ADO? – dakami

+0

Хм, я плохо, я недостаточно внимательно изучил ваш вопрос. Проблема не в ADO (ADO - это просто переход), это с движком базы данных. Из того, что я могу найти, ACE OLEDB в Excel не поддерживает COLLATE. Кажется, вы нашли свое собственное решение :) – leowyn

ответ

4

Я нашел обходное решение для ADO. Кажется COLLATE не существует (см: http://www.utteraccess.com/forum/Collate-Access-t1940463.html).

Можно, использование StrComp и установить его в двоичном сравнении:

sQuery = "Select a1.[Key], a2.[Val] from [AlphaNum1$] a1 LEFT JOIN [AlphaNum2$] a2 **ON StrComp(a1.[Key], a2.[Key], 0)=0**" 

Если есть лучшее решение, я рад за больше предложений :)

+0

Вот как это сделать. – Gustav

+0

Есть ли способ сделать это так, чтобы это не приводило к снижению производительности? У меня довольно много данных для обработки, и с StrComp требуется несколько часов (что заняло секунды раньше). – dakami

1

Вы можете добавить новое индексное текстовое поле для каждой таблицы, затем заполните это значение с помощью приведенной ниже функции и затем соедините эти таблицы обычным способом с использованием этих полей:

Public Function StrToByte(ByVal strChars As String) As String 

    Dim abytChar() As Byte 
    Dim lngChar  As Long 
    Dim strByte  As String 

    abytChar() = StrConv(strChars, vbFromUnicode) 

    strByte = Space(2 * (1 + UBound(abytChar) - LBound(abytChar))) 
    For lngChar = LBound(abytChar) To UBound(abytChar) 
    Mid(strByte, 1 + 2 * lngChar) = Hex(abytChar(lngChar)) 
    Next 

    StrToByte = strByte 

End Function 

Он будет производить ключи, как:

StrToByte("a12b") -> 61313262 
StrToByte("A12b") -> 41313262 
+0

Хорошая идея, спасибо. Я понимаю, что функция преобразует строку в байт, но я не совсем понимаю детали. Насколько он надежный? Есть ли вероятность, что он даст тот же результат для другой строки? – dakami

+0

Нет, он преобразуется в массив байтов, который записывается как строка, используя шестнадцатеричное представление каждого символа. Я не понимаю, почему вы считаете, что это не должно быть уникальным. – Gustav

-1

Просто, чтобы сообщить, что я воспроизвел ваш пример, и получили «ожидаемый результат» в первый раз, без каких-либо изменений в коде

Я использую Excel 2007 и ADO 2.8

+0

это комментарий не ответ ... – Sethmr

+0

Да, но не было возможности для комментариев в других сообщениях из-за «репутации» ... – robertocm