Страницы: 1 2 След.
RSS
Подскажите по синтаксису строк соединения c файлом Excel в ADO
 
Код
Public Sub Conn () 
Set Cnn= New ADODB.Connection
       Cnn.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\Test\Test.xlsm;" & _
       "Extended Properties=Excel 12.0; xml;"
        Cnn.Open
       
       strQuery = "select Наименование, Марка  FROM [Лист1$] Where Артикул  =  ""RG58"""   
       Set rst = cnn.Execute(strQuery)    
       array1 = rst.GetRows()                
End
Изменено: YGrigor18 - 31.10.2024 09:26:50
 
Код
Public Sub Conn()
    Dim Cnn As ADODB.Connection
    Dim rst As ADODB.Recordset
    Dim strQuery As String
    Dim array1 As Variant
    
    ' Инициализация подключения
    Set Cnn = New ADODB.Connection

    ' Строка подключения
    Cnn.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\Test\Test.xlsm;" & _
                           "Extended Properties='Excel 12.0 Xml; HDR=YES;'"
   
    ' Открытие подключения
    Cnn.Open

    ' SQL-запрос (убедитесь, что имена столбцов корректны и матчятся с заголовками в Excel)
    strQuery = "SELECT [Наименование], [Марка] FROM [Лист1$] WHERE [Артикул] = 'RG58'"

    ' Выполнение запроса
    Set rst = Cnn.Execute(strQuery)
    
    ' Получение данных в массив
    array1 = rst.GetRows()

    ' Закрываем Recordset и соединение
    rst.Close
    Cnn.Close

    ' Освобождаем ресурсы
    Set rst = Nothing
    Set Cnn = Nothing
End Sub
 
Online,   спасибо, ваш код более корректен и расшифрован.
я написал часть кода.
Вопрос в том, что в коде строка соединения содержит путь к файлу.
Но находясь в самом открытом файле Test.xlsm, ссылаюсь на него и соединяюсь как в внешнему!

Как итог, если вывести кол-во строк после выполнения запроса, то будет  -1.
MsgBox(rst.RecordCount)
Хотя результат запроса не нулевой, там есть строки и данные заносятся в массив array1
Возможно, что это из-за прилинкованного файла? (идею подсказали на форуме)

В тогда вопрос - как написать соединение не к эксель файлу по его пути, а соединение к открытой Эксель книге (листу) или диапазону ячеек листа???
 
Чтобы подключиться к открытой книге Excel и извлекать данные из листа или диапазона ячеек с помощью VBA, вам не нужно использовать ADO. Вы можете напрямую работать с объектами Excel.
Код
Public Sub GetDataFromActiveWorkbook()    Dim ws As Worksheet
   Dim rng As Range
   Dim array1 As Variant
   Dim i As Long
   Dim result As String
   
   ' Проверяем, есть ли открытая книга
   If Application.Workbooks.Count = 0 Then
       MsgBox "Нет открытых книг Excel"
       Exit Sub
   End If


   ' Устанавливаем активный лист (или вы можете указать конкретный лист)
   Set ws = ActiveWorkbook.Sheets("Лист1")
   
   ' Указываем диапазон: замените A1:C10 на нужный вам диапазон
   Set rng = ws.Range("A1:C10")
   
   ' Получаем данные в массив
   array1 = rng.Value
   
   ' Выводим данные (пример), чтобы показать результат
   For i = LBound(array1, 1) To UBound(array1, 1)
       result = result & array1(i, 1) & " " & array1(i, 2) & vbNewLine
   Next i


   ' Показываем результат в сообщении
   MsgBox result


   ' Освобождаем ресурсы
   Set rng = Nothing
   Set ws = Nothing
End Sub
Изменено: online - 31.10.2024 09:50:13 (оформление кода)
 
online, оформите код соответствующим тегом (<...>)
Согласие есть продукт при полном непротивлении сторон
 
Цитата
написал:
оформите код соответствующим тегом
принято. сделано.
 
online,  спасибо, идею понял, буду адаптировать код под свою задачу и форму.
Если диапазон динамический, попробовал этот диапазон объявить "умной таблицей",
через Ctrl+T и обращаться к этой таблице, например, Таблица 1.

и тогда вместо    Set rng = ws.Range("A1:C10")
можно написать  Set rng = ws.Range("Таблица1")  
При увеличении или уменьшении Таблицы 1, все подстраивается автоматически и корректно работает.

 
2.   ' Получаем данные в массив
       array1 = rng.Value
Будут ли доступны данные массива в другой Sub ?
Дело в том, что потом то данные выведутся в те же ComboBox на форме и  там уже к форме привязываются свои Sub (Change, Click, AfterUpdate).
Для отбора, фильтрации и т.д.
Будет ли виден массив array1 в перечисленных Sub ?  Если нет , то как это сделать?
Иначе придется плодить массивы, основанные на одних и тех же данных.
 
YGrigor18, да, вы можете использовать "умные таблицы" для динамического обращения к данным.
Переменные, созданные внутри одной области видимости (например, внутри одной Sub), по умолчанию не будут доступны в других Sub.
Если вам нужно использовать массив array1 в других процедурах, вы можете сделать его глобальной переменной.
 
online,   спасибо за помощь!
попробовал реализовать на своем примере, не получилось передать значения массива в другие Sub
пишу на вашем примере, сделал так:
Код
Option Explicit

   Dim ws As Worksheet
   Dim rng As Range
   Dim array1 As Variant
   Dim i As Long
   Dim result As String

Public Sub GetDataFromActiveWorkbook()    Dim ws As Worksheet   Dim rng As Range
  
   ' Проверяем, есть ли открытая книга
   If Application.Workbooks.Count = 0 Then
       MsgBox "Нет открытых книг Excel"
       Exit Sub
   End If


   ' Устанавливаем активный лист (или вы можете указать конкретный лист)
   Set ws = ActiveWorkbook.Sheets("Лист1")
   
   ' Указываем диапазон: замените A1:C10 на нужный вам диапазон
   Set rng = ws.Range("A1:C10")
   
   ' Получаем данные в массив
   array1 = rng.Value
   
   ' Выводим данные (пример), чтобы показать результат
   For i = LBound(array1, 1) To UBound(array1, 1)
       result = result & array1(i, 1) & " " & array1(i, 2) & vbNewLine
   Next i


   ' Показываем результат в сообщении
   MsgBox result


   ' Освобождаем ресурсы
   Set rng = Nothing
   Set ws = Nothing
End Sub

Создал форму, нарисовал два поля и пытаюсь передать значения Result  и Array(1,1)
Result передается пустым.
Array(1,1) -  пишет, что не определен
Код
Public Sub UserForm_Initialize()
   Application.Visible = True
   Call GetDataFromActiveWorkbook
End Sub

Код
Private Sub TextBox1_Change()
        UserForm1.TextBox1.Value = result
        UserForm1.ComboBox1.Value = array1(1, 1)
End Sub

Что не так?
 
А так?
Код
Option Explicit

Dim ws As Worksheet
Dim rng As Range
Dim array1 As Variant
Dim i As Long
Dim result As String

' Глобальные переменные для доступа из других процедур
Public result As String
Public array1 As Variant

Public Sub GetDataFromActiveWorkbook() 
    ' Проверяем, есть ли открытая книга
    If Application.Workbooks.Count = 0 Then
        MsgBox "Нет открытых книг Excel"
        Exit Sub
    End If

    ' Устанавливаем активный лист (или вы можете указать конкретный лист)
    Set ws = ActiveWorkbook.Sheets("Лист1")
    
    ' Указываем диапазон: замените A1:C10 на нужный вам диапазон
    Set rng = ws.Range("A1:C10")
    
    ' Получаем данные в массив
    array1 = rng.Value
    
    ' Выводим данные (пример), чтобы показать результат
    result = ""
    For i = LBound(array1, 1) To UBound(array1, 1)
        result = result & array1(i, 1) & " " & array1(i, 2) & vbNewLine
    Next i

    ' Освобождаем ресурсы
    Set rng = Nothing
    Set ws = Nothing
End Sub

Public Sub UserForm_Initialize()
    Application.Visible = True
    Call GetDataFromActiveWorkbook
    
    ' Теперь мы можем присвоить значения полям
    UserForm1.TextBox1.Value = result
    UserForm1.ComboBox1.Value = array1(1, 1) ' Убедитесь, что этот элемент действительно существует в массиве
End Sub

Private Sub TextBox1_Change()
    ' Если нужно обрабатывать изменение текста
    ' Например, возможно, вы хотите просто обновить ComboBox здесь.
End Sub
 
online, теперь конечно  все на своих местах ! Спасибо!
Уже применяю полученную информацию !  
Код
Private Sub TextBox1_Change()   
    ' Если нужно обрабатывать изменение текста
    ' Например, возможно, вы хотите просто обновить ComboBox здесь.
End Sub

Что вы подразумеваете под просто обновить ComboBox ?  
Прописать в ComboBox1.Value какое то значение или обновить весь список значений  через UserForm1.ComboBox1.AddItem  
или ComboBox1.Update ? (Но такой  команды похоже , увы нет)
 
 
"просто обновить ComboBox", я имел в виду возможность применения вашего кода для изменения или добавления новых значений в выпадающий список ComboBox, если это необходимо в вашем приложении.
 
online, понятно.
попробую реализовать оба варианта с ADO и только с объектами Excel.
1.Насколько понимаю, проще в обоих вариантах сначала таблицу с листа закачать в массив и с ним далее работать.
например, отбирать определенные данные из массива и отображать в разных ComBox на форме.

2. а можно изначально в самом листе изначально данные искать в таблице и их грузить в массив и отображать в ComboBox
   По мне, так вариант1 предпочтительнее.

3. Краткий вопрос . В посте #1 и #2  в ADO  VBA варианте некорректно работает rst.RecordCount, показывает -1, хотя запрос не пустой и он пишется в массив.
   Как подсказали, что это из-за присоединенного внешнего файла источника.
   Но как тогда посчитать кол-ко записей ? Или хотя бы понимать, что запрос не пустой, ибо если пустой,  то array1 = rst.GetRows() выдаст ошибку.
   Пытался считать через IF Exist ("SELECT [Наименование], [Марка] FROM [Лист1$] WHERE [Артикул] = 'RG58'"), но здесь синтаксис не прокатывает.
 
Цитата
написал:
показывает -1
может возвращать -1. Это часто происходит, когда используются курсоры, они не поддерживают подсчет записей
Цитата
написал:
Но как тогда посчитать кол-ко записей ?
попробуй так:
Код
Dim rst As ADODB.Recordset
Set rst = New ADODB.Recordset

rst.Open "SELECT [Наименование], [Марка] FROM [Лист1$] WHERE [Артикул] = 'RG58'", cn, adOpenStatic, adLockReadOnly

If Not rst.EOF Then
    rst.MoveLast ' Переместить указатель на последний элемент
    Dim recordCount As Long
    recordCount = rst.RecordCount ' теперь это должно работать
    rst.MoveFirst ' Вернуться к первому элементу
Else
    MsgBox "Записи не найдены"
End If
 
Цитата
написал:
По мне, так вариант1 предпочтительнее.

Все верно. Импортировать данные с листа в массив и затем работать с этим массивом более эффективным по сравнению с тем, чтобы постоянно взаимодействовать с элементами управления на форме или выполнять запросы к источнику данных. Сначала загрузите данные в массив, а затем отбирайте нужные данные и отображайте их в различных СomboBox на форме. Это минимизирует обращение к объектам Excel и улучшить производительность. )))

 
online, решение с rst.RecordCount  красивое! применил, все работает как надо!
Спасибо за все рекомендации, рабочий и аккуратный код, это очень помогло и  прояснились на текущий момент узкие места в алгоритме.
Пробую реализовать!
Изменено: YGrigor18 - 01.11.2024 14:49:01
 
Цитата
написал:
Спасибо за все рекомендации,
🤝
 
online, Добрый день! Прошу прощения, по теме, что обсуждали есть вопрос, можно ли его  здесь задать или нужно открывать новую тему?
В общем получить аналог кода VBA ADO
rst.Open "SELECT [Группа], count([Группа]) FROM [Лист1$]  GROUP BY' [Группа]", cnn, adOpenStatic, adLockReadOnly
и далее пишем в массив

Как в посте #10  дописать строки, чтобы сгруппировать данные по колонке Группа?
Чтобы получить список групп в колонке Группа и кол-во строк в каждой группе и записать в массив
Попробовал так, но не видно результата.

Sub Group()Dim ws As Worksheet
Set ws = ActiveWorkbook.Sheets("Лист11")
   ws.Range("A1:A100").Group
End Sub
Изменено: YGrigor18 - 07.11.2024 21:27:40
 
Код
Option Explicit

Dim ws As Worksheet
Dim rng As Range
Dim array1 As Variant
Dim groupCount As Object
Dim key As Variant
Dim i As Long
Dim result As String

' Глобальные переменные для доступа из других процедур
Public result As String
Public array1 As Variant

Public Sub GetDataFromActiveWorkbook() 
    ' Проверяем, есть ли открытая книга
    If Application.Workbooks.Count = 0 Then
        MsgBox "Нет открытых книг Excel"
        Exit Sub
    End If

    ' Указываем активный лист
    Set ws = ActiveWorkbook.Sheets("Лист1")
    
    ' Указываем диапазон: замените A1:C10 на нужный вам диапазон
    Set rng = ws.Range("A1:C10")
    
    ' Получаем данные в массив
    array1 = rng.Value
End Sub

Public Sub GroupData()
    Dim groupData As New Collection
    Dim countDict As Object
    Set countDict = CreateObject("Scripting.Dictionary")
    
    ' Получаем данные
    Call GetDataFromActiveWorkbook
    
    ' Проходим по массиву и считаем количество вхождений группы
    For i = LBound(array1, 1) To UBound(array1, 1)
        If Not IsEmpty(array1(i, 1)) Then ' Проверяем, что ячейка не пустая
            If countDict.Exists(array1(i, 1)) Then
                countDict(array1(i, 1)) = countDict(array1(i, 1)) + 1
            Else
                countDict.Add array1(i, 1), 1
            End If
        End If
    Next i

    ' Преобразуем словарь в массив для хранения результатов
    ReDim resultArray(1 To countDict.Count, 1 To 2)
    i = 1
    For Each key In countDict.Keys
        resultArray(i, 1) = key ' Группа
        resultArray(i, 2) = countDict(key) ' Количество
        i = i + 1
    Next key

    ' Присваиваем результат в глобальную переменную
    result = ""
    For i = LBound(resultArray, 1) To UBound(resultArray, 1)
        result = result & "Группа: " & resultArray(i, 1) & ", Количество: " & resultArray(i, 2) & vbNewLine
    Next i

    ' Освобождаем ресурсы
    Set countDict = Nothing
    Set rng = Nothing
    Set ws = Nothing
End Sub

Public Sub UserForm_Initialize()
    Application.Visible = True
    Call GroupData
    
    ' Теперь мы можем присвоить значения полям
    UserForm1.TextBox1.Value = result
    ' Здесь вы можете добавить элементы в ComboBox на основе полученных групп
End Sub

Private Sub TextBox1_Change()
    ' Если нужно обрабатывать изменение текста
    ' Например, возможно, вы хотите просто обновить ComboBox здесь.
End Sub
Может так..
 
online, огромное спасибо! изучаю, пробую и адаптирую код под свою форму и т.д., все сложнее, чем думал по сравнению с sql запросом :)  
 
online, добрый день! адаптировал код под свой тестовый пример.
Можно спросить концептуально по какому пути пойти далее?
В присоединенном файле, на форму в тестовом примере, вывел в списки ComboBox нужные поля.
Может конечно можно более сжато это сделать, но не факт.
Далее хочу сделать следующее:
1. при выборе любого значения из списка ID,  а это поле уникальное, чтобы в остальные поля подставлялись соответствующие значения из нужной строки, т.е из массива array2.
Как это сделать алгоритмически представляю. В разделе AfterUpdate поместить код: чтения UserForm1.ComboBox2.Value и искать в массиве array2 такое значение и найдя вычислить индекс и по нему уже проставить данные в другие поля ComboBox-ов.
Хотя можно и сразу, вычислять индекс элемента списка ComboBox2 на котором стоит курсор.
Код
Dim J As Integer, st As String
 J = ComboBox2.ListIndex
 If J >= 0 Then st = ComboBox2.List(J)
Это примерно правильно мыслю?

2.  далее хочу попробовать сделать выбор нужных данных по фильтрации полей Вид деятельности - Специальность -Город
    Чтобы выбирая Вид деятельности, в списках полей  - Специальность -Город  сразу пересчитывались данные  и в них оставались для выбора только данные для  выбранной Специальности.
И соответственно, если далее выбрать далее Специальность, то список поля Город также автоматически пересчитывался.
ФИО, это скорее инфо поле, оно просто покажется в итоге, когда будет выбрана позиция.

Как такое реализовать в принципе, концептуально?

На ADO по идее это вложенные SELECT c соответствующими WHERE.
Предположу, что можно использовать команду FILTER и полученные массивы на выходе прописывать в ComboBox-ы
Изменено: YGrigor18 - 08.11.2024 18:21:00
 
забыл файл вложить
 
YGrigor18, посмотрю в ПНД. На выходных занят.  
 
код для получения индекса выбранного элемента:
Код
Private Sub ComboBox2_AfterUpdate()
    Dim J As Integer
    Dim idValue As String
    Dim i As Long

    J = ComboBox2.ListIndex
    If J >= 0 Then
        idValue = ComboBox2.List(J)
        
        ' Теперь мы ищем этот idValue в массиве array2
        For i = LBound(array2, 1) To UBound(array2, 1)
            If array2(i, 1) = idValue Then ' Предполагается, что ID в первом столбце
                ' Обновляем остальные ComboBox на основе найденного индекса
                UserForm1.ComboBox3.Value = array2(i, 2) ' Например, колонка Специальность
                UserForm1.ComboBox4.Value = array2(i, 3) ' Например, колонка Город
                ' Добавьте здесь дальнейшие обновления полей по своему усмотрению
                Exit For
            End If
        Next i
    End If
End Sub
код для фильтрации может выглядеть так:
Код
Private Sub ComboBox1_AfterUpdate()
    Dim i As Long
    Dim uniqueSpecialties As Collection
    Set uniqueSpecialties = New Collection
    
    ' Очищаем ComboBox2 перед добавлением новых данных
    ComboBox2.Clear

    ' Просматриваем все значения и собираем уникальные Специальности
    For i = LBound(array2, 1) To UBound(array2, 1)
        If array2(i, 1) = ComboBox1.Value Then ' Проверяем Вид деятельности
            On Error Resume Next ' Игнорируем ошибку, если элемент уже присутствует
            uniqueSpecialties.Add array2(i, 2), CStr(array2(i, 2)) ' Колонка Специальности
            On Error GoTo 0
        End If
    Next i

    ' Заполняем ComboBox2 уникальными значениями
    For Each specialty In uniqueSpecialties
        ComboBox2.AddItem specialty
    Next specialty
End Sub
В сам файл подробно не вникал, придётся тебе адаптировать.
 
online,  добрый день!  ранее не работал с коллекциями, но в целом разобрался и адаптирую код под форму.
пока, что то не так отрабатывает, и похоже нормально на выхи смогу заняться и написать, сейчас в командировке,
спасибо за помощь
 
online, добрый день! поднастроил совсем немного под поля формы.
Работает в некоторыми оговорками.
По идее - выбирается элемент в ComboBox1 - из списка и в ComboBox2  вычисляется и формируется список ID.
Это и происходит. Но если выбрать другой элемент ComboBox1,  на ComboBox2  он лайн изменения нет, пока не откроешь его список.

Далее выбираем ID из списка в ComboBox2 и ничего на форме не происходит.
Все вычисляется - ComboBox3 ComboBox4  ComboBox5 , но чтобы обновилась форма (поля формы) нужно перейти на ComboBox3.

- посмотрите плз код, почему нет визуального он-лайн изменения данных.
- и концептуально используется Коллекция. А можно было это сделать на функционале Словаря, там ведь как и в Коллекции значение - ключ?
- я попробовал также использовать Filter, но пока не сделал
 
посмотри, так или нет.... возможно я немного не понял...
 
online, добрый день!  
насколько понял процедуры Private Sub ComboBox1_AfterUpdate() и Private Sub ComboBox2_AfterUpdate()
нужно перенести из модуля1 в  код формы Form1, а в модуле 1 этот код удалить.
Далее в этих процедурах чуть подправил  с If array2(i, 1) = selectedGroup  на If array2(i, 2) = selectedGroup
и вроде как работает.

Сейчас это работает так:
1)  Выбираем в ComboBox1  элемент группы, например ИТ,  нажимаем Enter (без нажатия ничего не происходит)  и
    в ComboBox2  появляются номера ID, которые привязаны к ИТ.
2)  Далее переходим в ComboBox2 , выбираем нужный ID номер и опять нажимаем Enter (без нажатия ничего не происходит).
    И тогда заполняются ComboBox3, 4,5

Это почти норм, но вопрос - можно ли сделать так, чтобы обновлялись поля не по Enter, а чтобы каждый раз выбирая из списка элемент группы
(ComboBox1 ) - сразу без Enter заполнялось поле ID (ComboBox2)  ?
А выбирая в (ComboBox2)  определенный ID  - без нажатия Enter заполнялись ComboBox3, 4,5

Предположу, что можно для этого задействовать:
Private Sub ComboBox1_CLick()  и Private Sub ComboBox2_CLick()   ' ?
или может быть
Private Sub ComboBox1_Change()  и Private Sub ComboBox2_Change()  ' ?

Подскажите, плз, как это принято обычно делать.
Спасибо!
Изменено: YGrigor18 - 19.11.2024 15:46:33
 
Что-то у меня тоже автоматика не получается пока...
 
online, добрый день!  сделал следующее, в Private Sub ComboBox1_Click()  и Private Sub ComboBox2_Click() - поместил код полностью аналогичный кодам
Private Sub ComboBox1_AfterUpdate()  и Private Sub ComboBox2_AfterUpdate() - соответственно.

И все работает без нажатия Enter, а поля обновляются после выбора элемента из списка, т.е по Click()

Еще сделал  UserForm1.ComboBox1.ListIndex = 0  и  UserForm1.ComboBox2.ListIndex = 0
Чтобы в списках стоял нулевой индекс и соответственно первый элемент списка.

Не знаю насколько это супер правильно.
Ну и в этих Sub объявляются одни и те же переменные. Как минимум с этим нужно посмотреть.
Вынести их в глобальные?

И  видимо если в  ComboBox1  не будет элементов, то  UserForm1.ComboBox1.ListIndex = 0   выдаст ошибку.
И аналогично, если после выбранного элемента ComboBox1, не найдется такого в ComboBox2, то
UserForm2.ComboBox2.ListIndex = 0  также даст ошибку.
Это то, что я увидел.

Можно такой подход использовать в целом?
ну с коррекциями по переменным и проверкой существовования непустого списка в ComboBox1 и ComboBox2
Изменено: YGrigor18 - 21.11.2024 13:04:31
Страницы: 1 2 След.
Наверх