Как макросом сливать инфу из Excel в Access? Суть: Есть табличка (любая) в Excel, рассчитывается ежемесячно/ежедневно. По окончании расчёта её надо скинуть в Access. И так каждый месяц/день (добавлять ниже) Я пользую 2010-й офис, но решение нужно универсальное
Спасибо
Всем привет
Как макросом сливать инфу из Excel в Access? Суть: Есть табличка (любая) в Excel, рассчитывается ежемесячно/ежедневно. По окончании расчёта её надо скинуть в Access. И так каждый месяц/день (добавлять ниже) Я пользую 2010-й офис, но решение нужно универсальное
Только добавление в Access этих данных (они все новые для таблицы в нём)? Или более сложный случай, что-то надо обновить, что-то удалить, что-то добавить?
Serge_007 А что подразумевается
Цитата
По окончании расчёта её надо скинуть в Access
Только добавление в Access этих данных (они все новые для таблицы в нём)? Или более сложный случай, что-то надо обновить, что-то удалить, что-то добавить?anvg
Да, надо только добавлять Например такая таблица в январе:
Дата
Название
Данные
01/01/1900
Товар1
12345
01/01/1900
Товар2
54321
03/01/1900
Товар1
111
Её по окончании месяца надо слить в Access В феврале табличка с новыми данными:
Дата
Название
Данные
01/02/1900
Товар1
45
02/02/1900
Товар2
123
30/02/1900
Товар1
999
В Access, по итогам двух месяцев, должна получиться такая таблица:
Дата
Название
Данные
01/01/1900
Товар1
12345
01/01/1900
Товар2
54321
03/01/1900
Товар1
111
01/02/1900
Товар1
45
02/02/1900
Товар2
123
30/02/1900
Товар1
999
Из сложностей наверное только проверка на наличие в БД уже введённых данных, что бы нельзя было повторно сгрузить уже имеющуюся в БД инфу, что бы задвоения не было
Да, надо только добавлять Например такая таблица в январе:
Дата
Название
Данные
01/01/1900
Товар1
12345
01/01/1900
Товар2
54321
03/01/1900
Товар1
111
Её по окончании месяца надо слить в Access В феврале табличка с новыми данными:
Дата
Название
Данные
01/02/1900
Товар1
45
02/02/1900
Товар2
123
30/02/1900
Товар1
999
В Access, по итогам двух месяцев, должна получиться такая таблица:
Дата
Название
Данные
01/01/1900
Товар1
12345
01/01/1900
Товар2
54321
03/01/1900
Товар1
111
01/02/1900
Товар1
45
02/02/1900
Товар2
123
30/02/1900
Товар1
999
Из сложностей наверное только проверка на наличие в БД уже введённых данных, что бы нельзя было повторно сгрузить уже имеющуюся в БД инфу, что бы задвоения не былоSerge_007
Можно просто копипастить записи вручную: в Excel выделяются записи, которые необходимо вставить, в Access - последняя (новая) пустая запись. А чтобы не прошли двойники, в таблице Access сделать уникальный ключ (индекс) Дата+Название - при этом повторения по ходу вставки будут автоматически отфутболиваться в специальную таблицу "Ошибки вставки".
Можно просто копипастить записи вручную: в Excel выделяются записи, которые необходимо вставить, в Access - последняя (новая) пустая запись. А чтобы не прошли двойники, в таблице Access сделать уникальный ключ (индекс) Дата+Название - при этом повторения по ходу вставки будут автоматически отфутболиваться в специальную таблицу "Ошибки вставки".Gustav
Я бы сделал, например, так - на DAO (исполняется в Excel): [vba]
Код
Sub fromExcelToAccess()
Dim dbe As Object 'DAO.DBEngine Dim db As Object 'DAO.Database Dim rst As Object 'DAO.Recordset Dim i As Long
Set dbe = CreateObject("DAO.DBEngine.120") Set db = dbe.OpenDatabase("C:\...\file.accdb") Set rst = db.TableDefs("ИмяТаблицы").OpenRecordset
For i = 1 To 10 'цикл по записям Excel rst.AddNew rst("Поле1").Value = "первое поле из Excel" rst("Поле2").Value = "второе поле из Excel" rst("Поле3").Value = "третье поле из Excel" rst.Update Next
End Sub
[/vba] Можно и на более модерновом ADO, но DAO мне исторически ближе. И меньше буковок получается в плане "строки подключения"
Для файлов c расширением mdb можно использовать Set dbe = CreateObject("DAO.DBEngine.36"). Это по поводу универсальности.
Я бы сделал, например, так - на DAO (исполняется в Excel): [vba]
Код
Sub fromExcelToAccess()
Dim dbe As Object 'DAO.DBEngine Dim db As Object 'DAO.Database Dim rst As Object 'DAO.Recordset Dim i As Long
Set dbe = CreateObject("DAO.DBEngine.120") Set db = dbe.OpenDatabase("C:\...\file.accdb") Set rst = db.TableDefs("ИмяТаблицы").OpenRecordset
For i = 1 To 10 'цикл по записям Excel rst.AddNew rst("Поле1").Value = "первое поле из Excel" rst("Поле2").Value = "второе поле из Excel" rst("Поле3").Value = "третье поле из Excel" rst.Update Next
End Sub
[/vba] Можно и на более модерновом ADO, но DAO мне исторически ближе. И меньше буковок получается в плане "строки подключения"
Для файлов c расширением mdb можно использовать Set dbe = CreateObject("DAO.DBEngine.36"). Это по поводу универсальности.Gustav
Не в чистом виде универсально, в силу ограничений провайдеров данных (не может Jet работать с xls? при Access mdb, ACE тут все яднее). [vba]
Код
Public Sub AddNew() 'Если хотя бы один из файлов 2007-2010, то 'Const sConn As String = "Provider=Microsoft.ACE.OLEDB.12.0;Mode=Share Deny None;Data Source=c:\sap\db.accdb" Const sConn As String = "Provider=Microsoft.Jet.OLEDB.4.0;;Mode=Share Deny None;Data Source=c:\sap\db.mdb" Dim AddSQL As String Dim pConn As Object, AddRSet As Object, TableRSet As Object Set pConn = CreateObject("ADODB.Connection"): pConn.Open sConn 'Поменять на Excel 12.0 для версий файлов 2007-2010 (путь и имя файла с импортируемыми в Access значениями) AddSQL = "Select t1.[Дата],t1.[Название],t1.[Данные] From [Excel 8.0;DATABASE=c:\SAP\book1.xls;HDR=YES].[Sheet1$] As t1" 'Заменить TableName на название таблицы в Access AddSQL = AddSQL & " Left Join TableName As t2 On ((t1.[Дата]=t2.[Дата]) And (t1.[Название]=t2.[Название]) And (t1.[Данные]=t2.[Данные]))" AddSQL = AddSQL & " Where t2.[Дата] Is Null" Set AddRSet = CreateObject("ADODB.Recordset"): AddRSet.CursorLocation = 3 AddRSet.Open AddSQL, pConn, 3, 1 If AddRSet.RecordCount = 0 Then MsgBox "Нет новых записей" Else Set TableRSet = CreateObject("ADODB.Recordset"): TableRSet.CursorLocation = 3 'Заменить TableName на название таблицы в Access TableRSet.Open "Select [Дата],[Название],[Данные] From TableName Where [Дата] Is Null", pConn, 3, 3 Do Until AddRSet.EOF TableRSet.AddNew TableRSet("Дата").Value = AddRSet("Дата").Value TableRSet("Название").Value = AddRSet("Название").Value TableRSet("Данные").Value = AddRSet("Данные").Value AddRSet.MoveNext Loop TableRSet.Update TableRSet.Close End If AddRSet.Close: pConn.Close End Sub
[/vba]
Не в чистом виде универсально, в силу ограничений провайдеров данных (не может Jet работать с xls? при Access mdb, ACE тут все яднее). [vba]
Код
Public Sub AddNew() 'Если хотя бы один из файлов 2007-2010, то 'Const sConn As String = "Provider=Microsoft.ACE.OLEDB.12.0;Mode=Share Deny None;Data Source=c:\sap\db.accdb" Const sConn As String = "Provider=Microsoft.Jet.OLEDB.4.0;;Mode=Share Deny None;Data Source=c:\sap\db.mdb" Dim AddSQL As String Dim pConn As Object, AddRSet As Object, TableRSet As Object Set pConn = CreateObject("ADODB.Connection"): pConn.Open sConn 'Поменять на Excel 12.0 для версий файлов 2007-2010 (путь и имя файла с импортируемыми в Access значениями) AddSQL = "Select t1.[Дата],t1.[Название],t1.[Данные] From [Excel 8.0;DATABASE=c:\SAP\book1.xls;HDR=YES].[Sheet1$] As t1" 'Заменить TableName на название таблицы в Access AddSQL = AddSQL & " Left Join TableName As t2 On ((t1.[Дата]=t2.[Дата]) And (t1.[Название]=t2.[Название]) And (t1.[Данные]=t2.[Данные]))" AddSQL = AddSQL & " Where t2.[Дата] Is Null" Set AddRSet = CreateObject("ADODB.Recordset"): AddRSet.CursorLocation = 3 AddRSet.Open AddSQL, pConn, 3, 1 If AddRSet.RecordCount = 0 Then MsgBox "Нет новых записей" Else Set TableRSet = CreateObject("ADODB.Recordset"): TableRSet.CursorLocation = 3 'Заменить TableName на название таблицы в Access TableRSet.Open "Select [Дата],[Название],[Данные] From TableName Where [Дата] Is Null", pConn, 3, 3 Do Until AddRSet.EOF TableRSet.AddNew TableRSet("Дата").Value = AddRSet("Дата").Value TableRSet("Название").Value = AddRSet("Название").Value TableRSet("Данные").Value = AddRSet("Данные").Value AddRSet.MoveNext Loop TableRSet.Update TableRSet.Close End If AddRSet.Close: pConn.Close End Sub
Спасибо Почему-то не даёт определить два ключевых поля... Но и с одним фигово получилось. Вставляется только первая строка с датой 01/01/1900, а вторая, с аналогичной датой уже даёт ошибку. Какие есть ещё варианты?
Спасибо Почему-то не даёт определить два ключевых поля... Но и с одним фигово получилось. Вставляется только первая строка с датой 01/01/1900, а вторая, с аналогичной датой уже даёт ошибку. Какие есть ещё варианты?Serge_007
Чтобы задать двойной ключ, надо выделить сразу оба поля и нажать кнопку с ключом. Двойной ключ обеспечит уникальность именно сочетания дата-наименование
Чтобы задать двойной ключ, надо выделить сразу оба поля и нажать кнопку с ключом. Двойной ключ обеспечит уникальность именно сочетания дата-наименованиеPelena
"Черт возьми, Холмс! Но как??!!" Ю-money 41001765434816
Всем спасибо С ключевыми полями разобрался, ошибки будем выводить пользователю Вот только даже три ключевых поля не спасут отца русской демократии
Теоретически, хотя и маловероятно, в один день могут быть привезены несколько раз одинаковое количество одного и того же товара. Как вариант, думаю можно нумеровать строки в Excel, а ключевым полем сделать аналогичное в Access
Это если не найдётся других вариантов. Я думал что можно как-то сверять данные на листе и данные в БД блоком (например Range("a1:c3")=трём записям в трёх полях) и если строки блока присутствует в БД - выдавать предупреждение
Всем спасибо С ключевыми полями разобрался, ошибки будем выводить пользователю Вот только даже три ключевых поля не спасут отца русской демократии
Теоретически, хотя и маловероятно, в один день могут быть привезены несколько раз одинаковое количество одного и того же товара. Как вариант, думаю можно нумеровать строки в Excel, а ключевым полем сделать аналогичное в Access
Это если не найдётся других вариантов. Я думал что можно как-то сверять данные на листе и данные в БД блоком (например Range("a1:c3")=трём записям в трёх полях) и если строки блока присутствует в БД - выдавать предупреждениеSerge_007
"А пожалуйста!" (с) О чем говорят мужчины Например, такой перебор записей набора с учетом фильтра (WHERE): [vba]
Код
Sub traverseRecordset()
Dim dbe As Object 'DAO.DBEngine Dim db As Object 'DAO.Database Dim rst As Object 'DAO.Recordset
Set dbe = CreateObject("DAO.DBEngine.120") Set db = dbe.OpenDatabase("C:\...\file.accdb") Set rst = db.OpenRecordset("SELECT * FROM Таблица1 WHERE 1=1")
Do While Not rst.EOF 'цикл по записям
'бла-бла-бла: что-то делаем с очередной записью 'например, "трогаем" значение первого поля Debug.Print rst.Fields(0).Value
rst.MoveNext Loop
End Sub
[/vba]
Цитата (Serge_007)
Пусть циклом. Как это сделать в Access?
"А пожалуйста!" (с) О чем говорят мужчины Например, такой перебор записей набора с учетом фильтра (WHERE): [vba]
Код
Sub traverseRecordset()
Dim dbe As Object 'DAO.DBEngine Dim db As Object 'DAO.Database Dim rst As Object 'DAO.Recordset
Set dbe = CreateObject("DAO.DBEngine.120") Set db = dbe.OpenDatabase("C:\...\file.accdb") Set rst = db.OpenRecordset("SELECT * FROM Таблица1 WHERE 1=1")
Do While Not rst.EOF 'цикл по записям
'бла-бла-бла: что-то делаем с очередной записью 'например, "трогаем" значение первого поля Debug.Print rst.Fields(0).Value
Я думал что можно как-то сверять данные на листе и данные в БД блоком
В виде SQL запроса можно, как вариант к версии, выбирающий записи в таблице и листе по совпадению трёх полей [vba]
Код
ExistsSQL = "Select t1.[Дата],t1.[Название],t1.[Данные] From [Excel 8.0;DATABASE=c:\SAP\book1.xls;HDR=YES].[Sheet1$] As t1" ExistsSQL = ExistsSQL & " Inner Join TableName As t2 On ((t1.[Дата]=t2.[Дата]) And (t1.[Название]=t2.[Название]) And (t1.[Данные]=t2.[Данные]))" Set ExistsRSet = CreateObject("ADODB.Recordset"): ExistsRSet.CursorLocation = 3 ExistsRSet.Open ExistsSQL, pConn, 3, 1 If ExistsRSet.RecordCount > 0 Then Set pSheet = ActiveWorkbook.Worksheets pSheet.Range("A1:C1").Value = Array("Дата", "Название", "Данные") pSheet.Range("A2").CopyFromRecordset ExistsRSet MsgBox "Создан лист дубликатов" End If ExistsRSet.Close
[/vba] Собственно, почти тоже самое используется в запросе на добавление AddSQL для отбора с листа записей, которые по всем трём полям не совпадают с существующими в таблице.
По поводу первичного ключа. У вас же товар приходит по какой-то накладной или нечто подобное. Её и можно добавить в качестве элемента ключа: дата, название, накладная. Или вариант: дата, название, время ввода в таблицу. Или некоторое строковое значение - код ввода, получаемое как запрос к FileSystemObject.GetTempName: дата, название, кодВвода [vba]
Код
Public Function GetPKey() Dim fso As Object, sKey As String Set fso = CreateObject("Scripting.FileSystemObject") sKey = fso.GetTempName GetPKey = Mid$(sKey, 1, Len(sKey) - 4) End Function
[/vba]
Цитата
Я думал что можно как-то сверять данные на листе и данные в БД блоком
В виде SQL запроса можно, как вариант к версии, выбирающий записи в таблице и листе по совпадению трёх полей [vba]
Код
ExistsSQL = "Select t1.[Дата],t1.[Название],t1.[Данные] From [Excel 8.0;DATABASE=c:\SAP\book1.xls;HDR=YES].[Sheet1$] As t1" ExistsSQL = ExistsSQL & " Inner Join TableName As t2 On ((t1.[Дата]=t2.[Дата]) And (t1.[Название]=t2.[Название]) And (t1.[Данные]=t2.[Данные]))" Set ExistsRSet = CreateObject("ADODB.Recordset"): ExistsRSet.CursorLocation = 3 ExistsRSet.Open ExistsSQL, pConn, 3, 1 If ExistsRSet.RecordCount > 0 Then Set pSheet = ActiveWorkbook.Worksheets pSheet.Range("A1:C1").Value = Array("Дата", "Название", "Данные") pSheet.Range("A2").CopyFromRecordset ExistsRSet MsgBox "Создан лист дубликатов" End If ExistsRSet.Close
[/vba] Собственно, почти тоже самое используется в запросе на добавление AddSQL для отбора с листа записей, которые по всем трём полям не совпадают с существующими в таблице.
По поводу первичного ключа. У вас же товар приходит по какой-то накладной или нечто подобное. Её и можно добавить в качестве элемента ключа: дата, название, накладная. Или вариант: дата, название, время ввода в таблицу. Или некоторое строковое значение - код ввода, получаемое как запрос к FileSystemObject.GetTempName: дата, название, кодВвода [vba]
Код
Public Function GetPKey() Dim fso As Object, sKey As String Set fso = CreateObject("Scripting.FileSystemObject") sKey = fso.GetTempName GetPKey = Mid$(sKey, 1, Len(sKey) - 4) End Function