Вложенный SQL Выбор запроса в Excel с VBA - PullRequest
1 голос
/ 14 апреля 2020

Я пытаюсь сделать запрос SQL на нескольких рабочих листах Excel.

У меня настроена так (каждая таблица представляет собой отдельный лист): enter image description here

Три рабочих листа - один с условием, второй с исходными данными и третий с выходными данными.

В выходных мне нужны все записи из источника, где значение в столбце A присутствует в условии рядом с положительным числом.

Я пытался сделать что-то вроде этого:

Select * 
From [Source$] 
Where [ColumnA] In (
    Select [Column1] 
    From [Condition$] 
    Where [Column2] > 0
)

Первая проблема возникла из-за пустых значений в таблице условий. Он не может обработать сравнение чисел, если в этом столбце есть пустые значения - Data type mismatch in criteria expression. Пока я справился с этим, выполнив некрасивую вещь: Where Column2 <> '' And Column2 <> '0'

Но это не главный вопрос. У меня есть большая проблема, и это как-то связано с вложением одного Select внутри другого. Даже несмотря на то, что внутренний Select возвращает один столбец со значениями, подобными этим:

enter image description here

Я все еще получаю ошибку Type mismatch in criteria expression. (ошибка немного отличается от описанной выше).

Так что я могу прекрасно управлять внутренним выражением. Я могу также запустить внешнее выражение с жестко закодированными значениями (например, Where ColumnA In "'value1','value2',..."). Но когда я их вкладываю, я получаю ошибку. Насколько мне известно, SQL я думаю, что этот запрос должен работать, но по какой-то причине внутренний запрос в Excel возвращает данные, которые не совместимы с оператором in.

Это мой полный код (спасибо этот SO ответ ):

Option Explicit
Private Const adCmdText As Long = 1
Private Const adStateOpen As Long = 1

Public Sub DisplayView()
    Dim dbField       As Variant
    Dim fieldCounter  As Long
    Dim dbConnection  As Object
    Dim dbRecordset   As Object
    Dim dbCommand     As Object
    Dim OutputSheet   As Excel.Worksheet

    Set dbConnection = CreateObject("ADODB.Connection")
    Set dbRecordset = CreateObject("ADODB.Recordset")
    Set dbCommand = CreateObject("ADODB.Command")

    Set OutputSheet = ThisWorkbook.Worksheets("Output")

    'Do a quick check to determine the correct connection string
    'if one of these don't work, have a look here --> https://www.connectionstrings.com/excel/
    If Left$(ThisWorkbook.FullName, 4) = "xlsm" Then
        dbConnection.connectionstring = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & _
        ThisWorkbook.FullName & ";Extended Properties='Excel 12.0 Macro;HDR=YES';"
    Else
        dbConnection.connectionstring = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & _
        ThisWorkbook.FullName & ";Extended Properties='Excel 12.0;HDR=YES';"
    End If

    'Open the connection and query
    dbConnection.Open
    With dbCommand
        .ActiveConnection = dbConnection
        .CommandType = adCmdText
        .CommandText = "Select * From [Source$] Where [ColumnA] In (Select [Column1] from [Condition$] where [Column1] > 0)"
        Set dbRecordset = .Execute
    End With

    'Clear the Output Sheet
    OutputSheet.Cells.Clear

    'Add Headers to output
    For Each dbField In dbRecordset.Fields
        fieldCounter = fieldCounter + 1
        OutputSheet.Cells(1, fieldCounter).Value2 = dbField.Name
    Next

    'Dump the found records
    OutputSheet.Range("A2").CopyFromRecordset dbRecordset
    If dbConnection.State = adStateOpen Then dbConnection.Close
End Sub


'Run from here
Public Sub ExampleRunner()
    Dim t As Double
    t = Timer
    DisplayView
    Debug.Print "Getting data took: " & Timer - t & " seconds"
End Sub

1 Ответ

1 голос
/ 14 апреля 2020

Возможно NumberFormat из Столбец2 в Условие для листа установлено значение General или Text. Попробуйте переформатировать весь столбец (не подмножество диапазона) как Number, а затем сохраните изменения, чтобы механизм SQL распознал тип данных как число. Оттуда вы можете выполнить свой запрос SQL с предложением IN или, что еще лучше, использовать INNER JOIN:

SELECT s.[ColumnA], s.[ColumnB], s.[ColumnC], s.[ColumnD] 
FROM [Source$] s
WHERE s.[ColumnA] IN (
    SELECT [Column1] 
    FROM [Condition$] 
    WHERE [Column2] > 0
)

SELECT s.[ColumnA], s.[ColumnB], s.[ColumnC], s.[ColumnD] 
FROM [Source$] s
INNER JOIN [Condition$] c
  ON s.[ColumnA] = c.[Column1]
WHERE c.[Column2] > 0

Кроме того, рассмотрите драйвер Excel ODB C, который обрабатывает любой формат (.xls, .xlsx, .xlsm, .xlsb) и избегайте использования объекта команды ADO, если параметры не требуются:

Public Sub DisplayView()
    Dim dbConnection  As Object, dbRecordset As Object
    Dim strSQL        As String
    Dim dbField       As Variant, fieldCounter  As Long

    Set dbConnection = CreateObject("ADODB.Connection")
    Set dbRecordset = CreateObject("ADODB.Recordset")

    ' CONNECTION WITH EXCEL ODBC DRIVER
    dbConnection.Open "Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};" _
                          & "DBQ=" & ThisWorkbook.FullName & ";"

    strSQL = "SELECT s.[ColumnA], s.[ColumnB], s.[ColumnC], s.[ColumnD]" _
               & " FROM [Source$] s" _
               & " INNER JOIN [Condition$] c" _
               & "   ON s.[ColumnA] = c.[Column1]" _
               & " WHERE c.[Column2] > 0"

    ' OPEN RECORDSET
    dbRecordset.Open strSQL, dbConnection

    With Worksheets("Output")
        ' HEADERS
        For Each dbField In dbRecordset.Fields
            fieldCounter = fieldCounter + 1
            .Cells(1, fieldCounter).Value = dbField.Name
        Next dbField    
        ' DATA ROWS
        .Range("A2").CopyFromRecordset dbRecordset
    End With

    dbRecordset.Close: dbConnection.Close
    Set dbRecordset = Nothing: Set dbConnection = Nothing
End Sub
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...