Всем доброго все времени суток! Помогите пожалуйста советом, а лучше кодом))) Передо мной поставили такую задачу: Нужно, чтобы формировался отчет по нажатию кнопки или запуске Макроса. В отчет должна переносится информация из закрытых файлов excel имеющих одно и тоже название check-list с одних и тех же ячеек (диапазона ячеек). Файлов таких порядка 500 штук. Название папок есть в в текстовом файле и excel. Информация должна копироваться рядом с названием папки в отчете. Инфа в файле check-list была размещена по вертикали с верху в низ, а копироваться в отчет должна по горизонтали в лева на права.
Папки с файлами check-list хранятся во вложенных папках. См. ниже пример.
В ручную не реально обновлять информацию. Помогите пожалуйста разобраться как это реализовать. Я сам второй день как с макросами начал работать, все что пока получилось сделал это скопировать диапазон ячеек из одного закрытого файла в книгу.
Посоветую освоить chatGPT - напишет даже и весь код, если опишите задачу, и может даже заработает. Хотя может если у кого такой код уже есть - могут поделиться. Но чтоб не зря писать - нужно видеть что за файлы, где инфа, как выглядит, да хоть такая мелочь как на каком из возможно множества листов она расположена.
Я, к сожалению, еще не имею опыта работы с макросами, как циклические команды реализовывать не знаю, буду гуглить. Мне бы пример какой-то. По аналогии я смогу сделать. Файлы размешу, как до работы доберусь. Спасибо за совет
В чек-листах вносятся цифры, которые преобразуются к слова «Да» или «Нет» в соседнем столбике. Мне даже лучше цифры из чек-листов переносить в сводный отчет и там их уже конвертировать в слова. Мне еще нужно статистику вести по проверкам / параметрам из чек-листов …
Макросом тоже вполне можно делать. Чуть осложняет то что нет точного адреса этих папок, но можно просто тупо перебрать все вложенные каталоги и разложить данные по таблице. Только вот с примером проблема - данных для копирования не нашёл, параметры почем-то интересуют не все...
Option Explicit
Public fso As Object
Private folder_name As String
Private dic As Object
Sub myCheck()
Set fso = CreateObject("Scripting.FileSystemObject")
Set dic = CreateObject("Scripting.Dictionary")
Application.ScreenUpdating = False
Dim Application_Calculation As XlCalculation
Application_Calculation = Application.Calculation
Application.Calculation = xlCalculationManual
CheckFolder ThisWorkbook.Path
DicJob
Application.Calculation = Application_Calculation
Application.ScreenUpdating = True
End Sub
Private Sub CheckFolder(sPath As String)
Dim curFold As Folder
If fso.FolderExists(sPath) Then Set curFold = fso.GetFolder(sPath)
If curFold Is Nothing Then Exit Sub
Dim sFile As String
sFile = sPath & "\check-list.xlsx"
If fso.FileExists(sFile) Then
folder_name = curFold.Name
FileJob sFile
End If
Dim subFold As Folder
For Each subFold In curFold.SubFolders
CheckFolder CStr(subFold)
Next
End Sub
Private Sub FileJob(sFull As String)
Application.StatusBar = folder_name
On Error Resume Next
Workbooks(fso.GetFileName(sFull)).Close False
On Error GoTo 0
Dim wb As Workbook
Set wb = Workbooks.Open(sFull, False, True)
WorkbookJob wb
wb.Close False
Application.StatusBar = False
DoEvents
End Sub
Private Sub WorkbookJob(wb As Workbook)
Dim arr As Variant
arr = wb.Sheets(1).Range("C1:C17").Value
arr(1, 1) = folder_name
myTranspose arr
dic(dic.Count) = arr
End Sub
Private Sub myTranspose(arr As Variant)
Dim brr As Variant
ReDim brr(1 To UBound(arr, 2), 1 To UBound(arr, 1))
Dim yb As Long
Dim xb As Long
For yb = 1 To UBound(brr, 1)
For xb = 1 To UBound(brr, 2)
brr(yb, xb) = arr(xb, yb)
Next
Next
arr = brr
End Sub
Private Sub DicJob()
If dic.Count = 0 Then Exit Sub
Dim arr As Variant
arr = GetArrFromDicItems()
Dim wb As Workbook
Set wb = Workbooks.Add
With wb.Sheets(1).Range("B6").Resize(UBound(arr, 1), UBound(arr, 2))
.Value = arr
.Select
End With
End Sub
Private Function GetArrFromDicItems() As Variant
Dim brr As Variant
brr = dic.Items()
ReDim arr(1 To dic.Count, 1 To UBound(brr(0), 2))
Set dic = Nothing
Dim ya As Long
Dim xa As Long
For ya = 1 To UBound(arr, 1)
For xa = 1 To UBound(arr, 2)
arr(ya, xa) = brr(ya - 1)(1, xa)
Next
Next
GetArrFromDicItems = arr
End Function
BodkhiSatva, воспринимайте это как условие, не воспринимайте это как требование. "Нужно получить информацию из файла, который на момент запуска макроса закрыт."
BodkhiSatva написал: а с макросом придется их открывать..
- ну можно в макросе использовать SQL и формально их не открывать. Или ExecuteExcel4Macro - тоже вроде как не открывает ))
Как найти файлы вот схема попроще, там выше монстр ))
Код
Sub tt()
Dim objFolder
Set objFolder = CreateObject("Scripting.FileSystemObject").GetFolder(ThisWorkbook.Path)
SearchFiles objFolder
End Sub
Function SearchFiles(fld)
Dim objSubFolder, objFile
For Each objFile In fld.Files
If objFile Like "*check-list.xlsx" Then Debug.Print objFile.Path
Next
For Each objSubFolder In fld.SubFolders
SearchFiles objSubFolder
Next
End Function
А так я тоже продумывал что сперва нужно в словаре запомнить позиции куда выгружать, затем вот так перебрать файлы, и взять и положить данные. Только вот нечего и непонятно почему усложнение что часть данных нужно выкидывать?
BodkhiSatva написал: по-моему, с PQ будет гораздо быстрее..
- не знаю, по ощущениям когда жмёшь обновить самый простой запрос эксель задумывается. Вот появятся тут нормальные файлы, напишет кто-то на PQ - у euric83 будет возможность проверить что быстрее.
Option Explicit
Public fso As Object
Private folder_name As String
Private rOut As Range
Sub myCheckDontOpen()
Set fso = CreateObject("Scripting.FileSystemObject")
Set rOut = ActiveSheet.Range("B6")
Application.ScreenUpdating = False
Dim Application_Calculation As XlCalculation
Application_Calculation = Application.Calculation
Application.Calculation = xlCalculationManual
CheckFolder ThisWorkbook.Path
Application.Calculation = Application_Calculation
Application.ScreenUpdating = True
End Sub
Private Sub CheckFolder(sPath As String)
Dim curFold As Folder
If fso.FolderExists(sPath) Then Set curFold = fso.GetFolder(sPath)
If curFold Is Nothing Then Exit Sub
Dim sFile As String
sFile = sPath & "\check-list.xlsx"
If fso.FileExists(sFile) Then
folder_name = curFold.Name
FileJob sFile
End If
Dim subFold As Folder
For Each subFold In curFold.SubFolders
CheckFolder CStr(subFold)
Next
End Sub
Private Sub FileJob(sFull As String)
Application.StatusBar = folder_name
rOut.Value = folder_name
Dim xo As Long
For xo = 1 To 16
rOut.Cells(1, 1 + xo).FormulaR1C1 = "='" & fso.GetParentFolderName(sFull) & "\[" & fso.GetFileName(sFull) & "]Лист1'!R" & xo + 1 & "C3"
Next
Set rOut = rOut.Cells(2, 1)
Application.StatusBar = False
DoEvents
End Sub
let f=(x)=>[a=List.LastN(Text.Split(x{0},""),2){0},aa=Number.From(Text.Split(a," "){0}), b=Table.ToColumns(Table.Range(Excel.Workbook(x{1})[Data]{0},1,16)), c=List.Transform(List.FirstN(b{1},14),(x)=>Text.Split(x," "){1}), d=#table({"N","ПОЗ"}&c&{"Дата","ФИО"},{{aa,a}&b{2}})][d], from = Folder.Files("C:\--СВОЙ-ПУТЬ--\Отчет по проверке"), slct = Table.SelectRows(from,each [Name]="check-list.xlsx"), lst = Table.ToList(slct,(x)=>{List.Last(x),List.First(x)}), srt = Table.Sort(Table.Combine(List.Transform(lst,f)),"N"), to = Table.SelectColumns(srt,List.Skip(Table.ColumnNames(srt))) in to
Hugo написал: ну можно в макросе использовать SQL и формально их не открывать
Один из вариантов:
Скрытый текст
Код
Option Explicit
Sub myCheck_in_One()
Dim fso As Object: Set fso = CreateObject("Scripting.FileSystemObject")
Dim dic As Object: Set dic = CreateObject("Scripting.Dictionary")
Dim curFold As Object, subFold As Object
Dim arr As Variant, brr As Variant
Dim yb As Long, xb As Long, ya As Long, xa As Long
Dim Application_Calculation As XlCalculation
Application.ScreenUpdating = False
Application_Calculation = Application.Calculation
Application.Calculation = xlCalculationManual
If fso.FolderExists(ThisWorkbook.Path) Then Set curFold = fso.GetFolder(ThisWorkbook.Path)
If curFold Is Nothing Then Exit Sub
Dim sFile As String: sFile = ThisWorkbook.Path & "\check-list.xlsx"
If fso.FileExists(sFile) Then
Dim folder_name As String: folder_name = curFold.Name
Application.StatusBar = folder_name
Dim sConnString As String: sConnString = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & sFile & ";" & _
"Extended Properties=""Excel 12.0 Xml;HDR=NO;IMEX=1"";"
Dim conn As Object: Set conn = CreateObject("ADODB.Connection")
conn.Open sConnString
Dim rs As Object: Set rs = CreateObject("ADODB.Recordset")
' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim query As String: query = "SELECT * FROM [Лист1$C1:C17]" ' Возможно у вас будет задано другое имя рабочего листа _
в этой строке "[Лист1$C1:C17]", измените имя рабочего листа если есть необходимость
' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
rs.Open query, conn, 3, 1
Dim arrData As Variant: arrData = rs.GetRows
rs.Close
conn.Close
Set rs = Nothing
Set conn = Nothing
arr = Application.Transpose(arrData)
arr(1, 1) = folder_name
ReDim brr(1 To UBound(arr, 2), 1 To UBound(arr, 1))
For yb = 1 To UBound(brr, 1)
For xb = 1 To UBound(brr, 2)
brr(yb, xb) = arr(xb, yb)
Next xb
Next yb
arr = brr
dic(dic.Count) = arr
Application.StatusBar = False
DoEvents
End If
For Each subFold In curFold.SubFolders
If fso.FolderExists(CStr(subFold)) Then
CheckFolder CStr(subFold), fso, dic, folder_name
End If
Next subFold
If dic.Count = 0 Then Exit Sub
brr = dic.Items()
ReDim arr(1 To dic.Count, 1 To UBound(brr(0), 2))
For ya = 1 To UBound(arr, 1)
For xa = 1 To UBound(arr, 2)
arr(ya, xa) = brr(ya - 1)(1, xa)
Next xa
Next ya
Set dic = Nothing
Dim wb As Workbook: Set wb = Workbooks.Add
With wb.Sheets(1).Range("B6").Resize(UBound(arr, 1), UBound(arr, 2))
.Value = arr
' ' На ваш выбор, Автоматически регулировать ширину столбцов
' .Columns.AutoFit
.Select
End With
Application.Calculation = Application_Calculation
Application.ScreenUpdating = True
End Sub
На основе кода от МатросНаЗебре из его поста #11. Но, необходимо подключить библиотеку (смотрим скрин) для правильной работы кода. МатросНаЗебре, Извините Пожалуйста что по заимствовал ваш код. Сори. Мира и Здоровья всем!
euric83, И да, у вас в примере файла "report" в "Состояние контролируемого параметра " указано 12 параметров а по сути в просматриваемых файлах эти параметры в количестве 14 штук. Скорее всего вы в спешке когда создавали файл пример напутали с "Состояние контролируемого параметра ", количество не то. Удачи.
Ребята, спасибо Вам большое, что Вы откликнулись и помогаете в решении моей задачи!
Пока, к сожалению, я не смог опробовать ни один из вариантов, не понимаю как их использовать, пробую копировать код и запускать процесс, но безрезультатно, выдает ошибки.
Я похоже переоценил свои возможности. Не могу даже найти где Microsoft ActivX Data Object включить как показано на скриншоте в cообщении #18.
И в сообщении #17 я не понимаю как использовать код.
Я только начинаю вникать в данную тему (сегодня второй день). Может я что-то не понимаю / не замечаю очевидное не требующее прояснения в Вашем кругу профессионалов?
Я немного поправил файл report: изменил кол-во параметров с 12 до 14 в таблице, и в файлах chek-list в папках и внес рандомные цифры.
Буду признателен если немного поясните как применить Ваш код к данному тестовому отчету.
Option Explicit
Public fso As Object
Private folder_name As String
Private rOut As Range
Sub myCheckDontOpen()
Set fso = CreateObject("Scripting.FileSystemObject")
Set rOut = ActiveSheet.Range("B6")
Application.ScreenUpdating = False
Dim Application_Calculation As XlCalculation
Application_Calculation = Application.Calculation
Application.Calculation = xlCalculationManual
CheckFolder ThisWorkbook.Path
Application.Calculation = Application_Calculation
Application.ScreenUpdating = True
End Sub
Private Sub CheckFolder(sPath As String)
Dim curFold As Object
If fso.FolderExists(sPath) Then Set curFold = fso.GetFolder(sPath)
If curFold Is Nothing Then Exit Sub
Dim sFile As String
sFile = sPath & "\check-list.xlsx"
If fso.FileExists(sFile) Then
folder_name = curFold.Name
FileJob sFile
End If
Dim subFold As Object
For Each subFold In curFold.SubFolders
CheckFolder CStr(subFold)
Next
End Sub
Private Sub FileJob(sFull As String)
Application.StatusBar = folder_name
rOut.Value = folder_name
Dim xo As Long
For xo = 1 To 16
rOut.Cells(1, 1 + xo).FormulaR1C1 = "='" & fso.GetParentFolderName(sFull) & "\[" & fso.GetFileName(sFull) & "]Лист1'!R" & xo + 1 & "C3"
Next
Set rOut = rOut.Cells(2, 1)
Application.StatusBar = False
DoEvents
End Sub
- там с цифрами 6 файлов? У меня вытягивает в 6 строк, и ещё 3 забивает пустотой, возможно другие с проблемами, нужно изучить.... Ну да, у "10 XV-9301-05B" в файле пробелов больше, наверное и с другими тоже накосячено.
МатросНаЗебре пошёл другим путём, после него и мой код все файлы подтягивает )) Вопрос - нужно взять только к тем файлам названия каталогов которых в таблице, или нужно взять все файлы что есть в каталогах, и заполнить ПУСТУЮ таблицу?
Мой вариант тоже можно скорректировать чтоб работал как от МатросНаЗебре, только будет не ссылки, а значения ставить. Так даже проще, словарь не нужен будет.