Работа с внешними источниками данных
Материалы по работе с внешними источниками данных на примере Excel и SQL.
Рассмотрим способы передачи данных между Excel и внешней базой данной на SQL сервере с помощью ADO.
Задача первая. Подключаемся к внешней базе данных.
Для начала надо подключиться к внешней базе данных. Подключение возможно если на компьютере установлен драйвер. Список установленных драйверов для подключения к базам данных на компьютере под управлением Windows:
Панель управленияВсе элементы панели управленияАдминистрированиеИсточники данных (ODBC)
Проверить подключение к базе данных можно простым способом. Создаем пустой файл (например, «текстовый документ.txt»), затем изменяем имя и расширение на .udl (например, «connect.udl»). Двойной клик мышкой по новому файлу, далее приступаете к настройке и проверке подключения к базе данных. После того, как удалось настроить корректное подключение к базе данных, сохраняем файл «connect.udl». Открываем файл «connect.udl» обычным текстовым редактором (например, блокнотом), и видим в строке подключения все необходимые параметры. Про подключение к внешним базам данных можно посмотреть на ресурсе ConnectionStrings .
Теперь возвращаемся к нашему VBA для Excel. В редакторе VBA подключаем последнюю версию библиотеки:
Microsoft ActiveX Data Objects Library
Пример кода:
Sub TestConnection() Dim cn As ADODB.Connection Set cn = New ADODB.Connection cn.ConnectionString = "" 'Параметры строки подключения cn.Open 'Открываем подключение cn.Close 'Закрываем подключение Set cn = Nothing 'Стираем объект из памяти End Sub
Задача вторая. Загружаем данные из внешней базы данных на SQL сервере в Excel.
После того, как мы установили подключение к внешней базе данных можно приступать к чтению данных и выводу в Excel. Здесь потребуется знание языка запросов SQL. В результате выполнения SQL запроса к нам возвращается некая таблица с данными в объект RecordSet. Далее из объекта RecordSet можно выгружать данные непосредственно на лист или в сводную таблицу.
Пример кода простой процедуры:
Sub LoadData() Dim cn As ADODB.Connection Dim rst As ADODB.Recordset Set cn = New ADODB.Connection Set rst = New ADODB.Recordset cn.ConnectionString = "" 'Параметры строки подключения cn.Open rst.Open "SELECT TOP 10 * FROM <таблица>", cn 'SQL-запрос, подключение ActiveSheet.Range("A1").CopyFromRecordset rst 'Извлекаем данные на лист rst.Close cn.Close Set rst = Nothing Set cn = Nothing End Sub
Для удобства работы. Предлагаю создать собственный класс «tSQL» для работы с базой данных. У класса будет одно свойство:
Public ConnectionSring As String
Для чтения данных напишем метод SelectFrom с параметрами TableName и ws. TableName — это имя таблицы, откуда будем считывать данные и ws — лист Excel, куда будем записывать данные.
Public Sub SelectFrom(TableName As String, ws As Worksheet) Dim cn As ADODB.Connection Dim rst As ADODB.Recordset Dim SQLstring As String Dim i As Long Set cn = New ADODB.Connection Set rst = New ADODB.Recordset SQLstring = "SELECT * FROM " & TableName ws.Cells.Clear cn.ConnectionString = ConnectionSring cn.Open rst.Open SQLstring, cn For i = 1 To rst.Fields.Count ws.Cells(1, i) = rst.Fields(i - 1).Name Next i ws.Range("A2").CopyFromRecordset rst rst.Close cn.Close Set rst = Nothing Set cn = Nothing SQLstring = Empty i = Empty End Sub
Пример использования класса tSQL в процедуре
Sub mySQL() Dim ts As tSQL Set ts = New tSQL ts.ConnectionSring = '<Строка подключения> ts.SelectFrom "Название таблицы", ActiveSheet Set ts = Nothing End Sub
Задача третья. Загружаем данные из Excel во внешнюю базу данных.
Для записи данных напишем метод InsertInto с параметрами TableName. rHead и rData. TableName — это имя таблицы, куда будем добавлять данные; rHead — диапазон ячеек, с указанием полей; rData — диапазон ячеек с данными, которые будем добавлять.
Public Sub InsertInto(TableName As String, rHead As Range, rData As Range) Dim cn As ADODB.Connection Dim SQLstring As String Dim SQLstringH As String Dim SQLstringV As String Dim i As Long Dim j As Long Dim arrHead() Dim arrData() arrHead = rHead.Value arrData = rData.Value Set cn = New ADODB.Connection cn.ConnectionString = ConnectionSring cn.Open SQLstringH = "INSERT INTO " & TableName & "(" For j = LBound(arrHead, 2) To UBound(arrHead, 2) SQLstringH = SQLstringH & " " & arrHead(1, j) If j < UBound(arrHead, 2) Then SQLstringH = SQLstringH & "," Else SQLstringH = SQLstringH & ")" End If Next j SQLstringH = SQLstringH & " VALUES(" For i = LBound(arrData, 1) To UBound(arrData, 1) For j = LBound(arrData, 2) To UBound(arrData, 2) SQLstringV = SQLstringV & " " & arrData(i, j) If j < UBound(arrHead, 2) Then SQLstringV = SQLstringV & "," Else SQLstringV = SQLstringV & ") " End If Next j SQLstring = SQLstringH & SQLstringV SQLstringV = Empty cn.Execute SQLstring Next i cn.Close Set cn = Nothing SQLstring = Empty i = Empty j = Empty SQLstring = Empty SQLstringH = Empty SQLstringV = Empty Erase arrHead Erase arrData End Sub
Пример использования класса tSQL в процедуре
Sub mySQL() Dim ts As tSQL Set ts = New tSQL ts.ConnectionSring = '<Строка подключения> ts.InsertInto "Название таблицы", Range("B1:D1"), Range("B8:D300") Set ts = Nothing End Sub
Задача четвертая. Управляем внешней базой данных из Excel
Рекомендую использовать запросы в основном для чтения данных из внешней БД. Можно записывать данные в таблицы внешней БД.
Но крайне не желательно использовать Excel для управления внешней базой данных, лучше использовать стандартные средства разработки.
Полезные ссылки:
Data from Excel to SQL
http://www.excel-sql-server.com/excel-sql-server-import-export-using-vba.htm
UPDATE 21.10.15 Добавил «обратный» макрос — VBA в SQL и макрос для доступа к строке запроса SQL
Некоторое время назад я прошел несколько курсов по SQL. И мне было очень интересно — какую часть из мощного инструмента под названием T-SQL можно применять без использования SQL-Server (не дают мне сервачек под мои нужды, хнык-хнык).
Итак… Начнем с простого — подключение через Query Table в VBA. Можно записать через макрорекордер — для этого нужно создать подключение через Microsoft Query.
Выбираем Excel Files, указываем путь к файлу (пытаясь при этом не ругать разработчиков за интерфейс из 90х годов), фильтруем как-угодно поля. Нам сейчас это не важно — главное получить код, который дальше можно будет корректировать.
Должно получится что-то вроде этого:
Sub Макрос1() With ActiveSheet.ListObjects.Add(SourceType:=0, Source:=Array(Array( _ "ODBC;DSN=Excel Files;DBQ=D:DropboxExcelтест excel_SQL-2015.xlsx;DefaultDir=D:DropboxExcel;DriverId=1046;MaxBufferSize=2048;Page" _ ), Array("Timeout=5;")), Destination:=Range("$A$1")).QueryTable .CommandType = 0 .CommandText = Array( _ "SELECT Продажи.F2, Продажи.F3" & Chr(13) & "FROM `D:DropboxExcelтест excel_SQL-2015.xlsx`.Продажи Продажи" _ ) .RowNumbers = False .FillAdjacentFormulas = False .PreserveFormatting = True .RefreshOnFileOpen = False .BackgroundQuery = True .RefreshStyle = xlInsertDeleteCells .SavePassword = False .SaveData = True .AdjustColumnWidth = True .RefreshPeriod = 0 .PreserveColumnInfo = True .ListObject.DisplayName = "Таблица_Запрос_из_Excel_Files" .Refresh BackgroundQuery:=False End With End Sub
Строчка .CommandText = «SELECT…» — отвечает за SQL запрос. Если хотя бы немного почитать поисковую выдачу google по запросу QueryTable можно упростить код до следующего:
Sub CopyFromRecordset_To_Range() DBPath = "C:InputData.xlsx" sconnect = "Provider=MSDASQL.1;DSN=Excel Files;DBQ=" & DBPath & ";HDR=Yes';" Conn.Open sconnect sSQLSting = "SELECT * FROM [Sheet1$]" rs.Open sSQLSting, Conn Set QT1 = ActiveSheet.QueryTables.Add(rs, Range("A1")) QT1.Refresh rs.Close Conn.Close End Sub
Теперь начинаем копаться глубже — какого уровня запросы можно строить из VBA. Самые-самые базовые, основные конструкции — все работает, все ок.
Заполнение нового столбца одинаковым значением
SELECT 'YTikhonov', * FROM [Sheet1$]
Переименование столбцов
SELECT [Advertiser] AS 'Рекламодатель', [Quantity] AS 'Количество' FROM [Sheet1$]
Фильтрация записей
SELECT * FROM [Sheet1$] WHERE [Year] = 2014
Сортировка
SELECT * FROM [Sheet1$] ORDER BY [Advertiser] DESC
Агрегация записей
SELECT [Advertiser], Sum([Cost]) FROM [Sheet1$] GROUP BY [Advertiser]
Работа с датой
Дату можно впрямую через конструкцию
[SomeDateField] = {ts '2015-01-01 00:00:00'}
Но я люблю отталкиваться от текущей даты. За пару текущая дата-время отвечает функция SYSDATETIME() и она может вернуть в том числе текущий день. Для этого нужна еще одна функция — CONVERT(type,value)
SELECT CONVERT(date,SYSDATETIME())
С функцией DATEFROMPARTS строка запроса в Excel почему-то не дружит, поэтому придется использовать костыли функцию DATEADD:
DATEADD(minute, 59, DATEADD(hour, 23, DATEADD(month, MONTH(SYSDATETIME())+1, DATEADD(year, YEAR(SYSDATETIME()) - 1900, 0))))-1
Эта строчка в любой день октября 2015 вернет значение — 30.11.15 23:59
А теперь — немного best practice!
Объединение + Агрегация + Join + Подзапросы. И самое интересное — подключение к нескольким источникам:
SELECT [Year], O.Numbers, SCost, SVolume, SQuantity FROM ( SELECT [Year], Month, SUM([Cost RUB]) AS SCost, SUM(Volume) AS SVolume, SUM(Quantity) AS SQuantity FROM ( SELECT Advertiser, 2013 as [Year], Month, [Cost RUB], Quantity, Volume FROM [N:GKRadioМаркетингСлужебный2013.xlsb].[Мониторинг$] UNION SELECT Advertiser, 2014 as [Year], Month, [Cost RUB], Quantity, Volume FROM [N:GKRadioМаркетингСлужебный2014.xlsb].[Мониторинг$] UNION SELECT Advertiser, 2015 as [Year], Month, [Cost RUB], Quantity, Volume FROM [N:GKRadioМаркетингСлужебный2015.xlsb].[Мониторинг$] ) WHERE [Advertiser] = 'METRO GROUP' GROUP BY [Year], Month ) as T INNER JOIN [C:testMonth.xlsb].[Test$] AS O ON T.[Month] = O.[Month]
Одна проблема — если осуществлять такого вида запрос для соединения нескольких Excel-файлов, он будет выполняться достаточно медленно. У меня вышло порядка 2 минут. Но не стоит думать что это бесполезно — если подобные запросы выполнять при подключении к SQL-серверу, то время обработки будет 1-2 секунды (само собой, все зависит от сложности запроса, базы, и прочие прочие факторы).
Бонусы
Формировать более-менее сложный запрос SQL вручную в VBA мягко говоря неудобно. Поэтому я написал мини-макрос, который берет информацию из буфера обмена, и возвращает туда строчки для вставки в VBE.
'работа с буфером обмена http://excelvba.ru/code/clipboard Private Function ClipboardText() ' чтение из буфера обмена With GetObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}") .GetFromClipboard ClipboardText = .GetText End With End Function Private Sub SetClipboardText(ByVal txt$) ' запись в буфер обмена With GetObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}") .SetText txt$ .PutInClipboard End With End Sub Public Sub SQL_String_To_VBA() Dim sInput As String, sOut As String Dim ArrInput, i As Integer Dim cIdent As Integer: cIdent = 1 'Count of tabs Dim sVar As String: sVar = "strSQL" 'Name of variable sInput = ClipboardText() ArrInput = Split(sInput, Chr(13)) For i = LBound(ArrInput) To UBound(ArrInput) sOut = sOut & sVar & " = " & sVar & " & " & Chr(34) sOut = sOut & String(cIdent, Chr(9)) sOut = sOut & Replace(ArrInput(i), Chr(10), "") sOut = sOut & Chr(34) & "& chr(10)" & Chr(10) Next i SetClipboardText (sOut) End Sub Public Sub VBA_String_To_SQL() Dim sInput As String, sOut As String Dim ArrInput, i As Integer, sTemp sInput = ClipboardText() ArrInput = Split(sInput, Chr(10)) For i = LBound(ArrInput) To UBound(ArrInput) sTemp = Replace(ArrInput(i), "& chr(10)", "") If Right(sTemp, 1) = " " Then sTemp = Left(sTemp, Len(sTemp) - 1) If Right(sTemp, 1) = Chr(34) Then sTemp = Left(sTemp, Len(sTemp) - 1) If Len(sTemp) > 0 Then sTemp = Right(sTemp, Len(sTemp) - InStr(1, sTemp, Chr(34))) sOut = sOut & Chr(10) & sTemp End If Next i SetClipboardText (sOut) End Sub
Сами запросы просто и удобно создавать, например, используя Notepad++. Создали многострочный запрос SQL, копируете его в буфер обмена, запускаете макрос и вуаля — в буфере обмена строчки кода, готовые для вставки в ваши макросы. При желании вы можете настроить название переменной и количество табуляций.
И еще один небольшой бонус. Если у вас есть отчет по менеджерам/руководителям, построенный на запросах, то вам наверняка потребуется получать доступ к строке запроса через VBA. Сделать это можно через замечательную команду .CommandText — работает на чтение и запись. Мне для формирования отчета на 25 человек очень пригодился.
Public Sub ReplaceCommandText() Dim con As WorkbookConnection Dim sTemp As String For Each con In ActiveWorkbook.Connections sTemp = con.ODBCConnection.CommandText con.ODBCConnection.CommandText = sTemp con.Refresh Next con End Sub
PS Ссылка с ответом на вопрос — как вставить данные из Excel в SQL
https://www.simple-talk.com/sql/t-sql-programming/questions-about-using-tsql-to-import-excel-data-you-were-too-shy-to-ask/
Приятного использования!
Although we can create External Data Sets in Excel that pull information from a SQL Server Database there are times when you want to check a value or lookup a value from an underlying SQL Server database directly from an Excel formula.
Using VBA we can do this!
We will look at an example that enables us to lookup a customer’s total revenue value from an Excel Formula.
The VBA To Query a SQL Server Database
The following VBA Function can be used from within an Excel VBA Project. You will, however need to add the ADODB library to the project first.
Public Function LookupAWCustomerRevenue(intID As Long) As Currency
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim strConnString As String
If intID = 0 Then
LookupAWCustomerRevenue = 0
Else
strConnString = "Provider=SQLOLEDB;Data Source=W10NBMJDSQL2014;" _
& "Initial Catalog=AdventureWorks2014;Integrated Security=SSPI;"
Set conn = New ADODB.Connection
conn.Open strConnString
Set rs = conn.Execute("SELECT SUM(TotalDue) AS CustRev FROM Sales.SalesOrderHeader WHERE CustomerID = " & intID)
If Not IsNumeric(rs.Fields("CustRev").Value) Then
LookupAWCustomerRevenue = 0
Else
LookupAWCustomerRevenue = rs.Fields("CustRev").Value
rs.Close
End If
End If
End Function
In this example we use an ADO connection to connect to a SQL Server instance (DBSRVSQL2014):
strConnString = "Provider=SQLOLEDB;Data Source=DBSRVSQL2014;" _
& "Initial Catalog=AdventureWorks2014;Integrated Security=SSPI;"
- The Provider parameter indicates that an OLDEB connection will be established and the Data Source parameter points to the SQL Server Instance.
- The Initial Catalog parameter identifies the database to be queried (AdventureWorks2014)
- The Integrated Security parameter indicates that Windows Authemtication will be used to authenticate with SQL Server.
A RecordSet object (rs) is used to create a record set from a SELECT statement:
Set rs = conn.Execute("SELECT SUM(TotalDue) AS CustRev FROM Sales.SalesOrderHeader WHERE CustomerID = " & intID)
The SELECT statement is constructed from a literal string and the value for the variable intID that is passed into the function when it is called.
The If statement at the start checks for an intID value of 0. Integer variables default to a value of zero if not initialised (in other words if no value is provided when the function is called). If no value is passed in to the function a value of 0 is returned as the revenue value.
If intID = 0 Then
LookupAWCustomerRevenue = 0
The second If statement tests for a non numeric value being returned by the SELECT statement. If a customerID passed in to the function is valid, but they have plaved no orders the SUM(TotalDue) expression will return a NULL value. If this happens then the funvtion will return a value of 0 instead.
If Not IsNumeric(rs.Fields("CustRev").Value) Then
LookupAWCustomerRevenue = 0
The NULL value scenario can be seen in the following screen shot.
I placed a breakpoint on the line of VBA code containing the If statement and opened the Locals Window so that I could see all the variable values at that point of execution.
I tested the function by executing it from the Immediate Window in the VBA editor:
?LookupAWCustomerRevenue(1)
With a breakpoint set the code execution automatically stops at the marked line and enables us to view the environment at that point of execution.
The Locals Window in the above screen shot shows the Recordset object variable rs, and specifically the value for the first field from rs, “CustRev”. We can see that it is set to Null. This is because a Customer with CustomerID value 1 has not placed any orders and, therefore, has no resulting revenue value.
The following screnn shot shows that the query returns NULL when run directly on the SQL Server instance:
Assuming that a valid CustomerID is passed into the function and a non NULL value is returned by the SELECT statement, the function will return the total sales revenue for that customer as a currency value.
LookupAWCustomerRevenue = rs.Fields("CustRev").Value
Calling The VBA Function From An Excel Formula
Calling a VBA function from an Excel Formula is simple. When you construct an Excel Formula by typing it into a cell intellisense will display matching VBA functions as well as built in functions. The following screen shot shows this:
You can see that there are two VBA functions listed above that start with Lo: LookupAWCustomerRevenue and LookupPersonName.
The following example shows the LookupAWCustomerRevemue function being used in an cell to calvulate the total revenue for the CustomerID value sepcified in Column A of the worksheet:
Summary
In this article we have seen a bit of VBA coding, some VBA Error Handling and Debugging techniques, and we have seen how we can call VBA functions from an Excel formula. Feel free to email us at info@ptr.co.uk if you have any questions relating to this article. There is also an Access version of this article.
If you would like to learn more about any of the techniques seen in this article or VBA programming why not take a look at our Excel and Access VBA Training courses.
If you would like to learn more about working with a SQL Server Database then take a look at our SQL Server Training Courses.
You might find that these courses come up on a Late Availability offer from time to time, offering savings of 30%.
When it comes to big amount of data, Excel application is not the best solution to work with, in case of storage. Much better fit would be a database like Access or MSSM. In this article I’m going to show You how to connect to Microsoft SQL Server using VBA.
Database & server
In my case I got AdventureWorks2016 database installed on my PC. I connected to that base and the view of Object Explorer in Managament Studio looks like this.
Here You can see the server name, database name and tables name in Tables folder, which all will be necessary in further steps.
References
To be able to create connection and then operate on pulled data from database, You need to check in the Tools/References 2 checkboxes: Microsoft ActiveX Data Objects Library and Microsoft ActiveX Data Objects Recordset Library.
It doesn’t have to be the version 2.8, just like in the screen above. You have much more possibilities.
When choosing the references try to think about the future users (ot their Office version) of this code You create.
ADODB Connection
After setting up the references You can declare connection of ADODB library.
Dim connection As ADODB.connection
Set connection = New ADODB.connection
Connection String
This is the main thing of ADODB connection, to be able to connect to the database. You need to know at least the provider, server name, database name and authentication method.
For MS SQL use SQLOLEDB.1 as provider. That should always work, unless You know that the provider for your database is different.
The server name You can see at the top of the object explorer.
So in my case it is (LocalDb)LocalDbTest.
Let server_name = "(LocalDb)LocalDbTest"
The database name is the name under the Databases folder tree in Object Explorer.
In my case this is AdventureWorks2016.
Let database_name = "AdventureWorks2016"
The authentication method depends on that if You pass the credentials in the connection string or use windows authentication and on provider. You can choose from the values: true , false, yes, no and sspi.
So putting all together it can look like this.
.ConnectionString = "Provider=SQLOLEDB.1;Server=" & server_name & _
";database=" & database_name & ";Integrated Security=SSPI;"
Solution to connect local database
In case of MS SQL use SQLOLEDB.1 as provider, but if You got your database locally, as I have, go with SQLNCLI11. This is something I was fighting with and looking for hours to connect.
.ConnectionString = "Provider=SQLNCLI11;Server=" & server_name & _
";database=" & database_name & ";Integrated Security=SSPI;"
Open connection & state check
After completing the connection string, You can also set other properties like timeout. In the end open the connection using .Open.
With connection
.ConnectionString = "Provider=SQLNCLI11;Server=" & server_name & _
";database=" & database_name & ";Integrated Security=SSPI;"
.ConnectionTimeout = 10
.Open
End With
If there was no error check the state of database You connected.
If connection.State = 1 Then
Debug.Print "Connected!"
End If
SQL query
Having opened database connection You need to ask for data, I mean to build SQL query. In my case I just want to take all (*) data from TestTable table. The database name in this query is not really needed, because it is already in the connection string.
Dim sqlQuery As String
sqlQuery = "Select * from [AdventureWorks2016].[dbo].[TestTable]"
If You are not familiar with SQL I can say that the basics are even easier than VBA. As I said, basics of SQL.
Recordset – copy paste data from database
If You want to get the data from the query, You need to create Recordset of ADODB library. There are 2 main things – sqlQuery and connection. About the rest of properties You can read here.
Dim rsSql As New ADODB.Recordset
rsSql.CursorLocation = adUseClient
rsSql.Open sqlQuery, connection, adOpenStatic
After You .Open the recordset – get the data from database with SQL query using created connection, You can paste everything in chosen location.
ThisWorkbook.Sheets(1).Range("A1").CopyFromRecordset rsSql
And just like that all the data from the specified table is in the chosen range of your Workbook.
And remember that this method is not pasting headers, only the values of the columns below headline!
Connect to MS SQL code
Option Explicit
Sub connect2mssql()
Dim connection As ADODB.connection
Set connection = New ADODB.connection
Dim server_name As String, database_name As String
Let server_name = "(LocalDb)LocalDbTest"
Let database_name = "AdventureWorks2016"
With connection
.ConnectionString = "Provider=SQLNCLI11;Server=" & server_name & _
";database=" & database_name & ";Integrated Security=SSPI;"
'SQLOLEDB.1
.ConnectionTimeout = 10
.Open
End With
If connection.State = 1 Then
Debug.Print "Connected!"
End If
Dim sqlQuery As String
sqlQuery = "Select * from [AdventureWorks2016].[dbo].[TestTable]"
Dim rsSql As New ADODB.Recordset
rsSql.CursorLocation = adUseClient
rsSql.Open sqlQuery, connection, adOpenStatic
ThisWorkbook.Sheets(1).Range("A1").CopyFromRecordset rsSql
End Sub
So, this is how You connect to Microsoft SQL Server using Excel VBA!
At first sight it seems like complex stuff, but in the end it is not that hard and it opens wide range of possibilities to automate data from other sources than only Excel workbooks. Combining the VBA knowledge with some more complex SQL queries can lead to really big tools dealing with tons of data. It is much easier to connect to the source than converting the data to Excel format and then start the macro. Also it speeds up the whole work a lot.
I’m very advanced in VBA, Excel, also easily linking VBA with other Office applications (e.g. PowerPoint) and external applications (e.g. SAP). I take part also in RPA processes (WebQuery, DataCache, IBM Access Client Solutions) where I can also use my SQL basic skillset. I’m trying now to widen my knowledge into TypeScript/JavaScript direction.
View all posts by Tomasz Płociński
Learn how to easily run a plain SQL query with Visual Basic for Applications on your Excel Spreadsheet.
In the last days, I received an unusual request from a friend that is working on something curious because of an assignment of the University. For this assignment, it’s necessary to find the answer or data as response of a query. Instead of a database, we are going to query plain data from an excel spreadsheet (yeah, just as it sounds). For example, for this article, we are going to use the following Sheet in Excel Plus 2016:
The goal of this task is to write raw SQL Queries against the available data in the spreadsheet to find the answer of the following questions:
- Which users live in Boston.
- Which users are boys and live in Boston.
- Which users were born in 2012.
- Which users were born in 2010 and were ranked in place #1.
Of course, finding such information as a regular user is quite easy and simple using filters and so, however the assignment requires to do the queries using SQL and Visual Basic for the job. In this article, I will explain you from scratch how to use Microsoft Visual Basic for Applications to develop your own macros and run some SQL queries against plain data in your excel spreadsheets.
1. Launch Microsoft Visual Basic For Applications
In order to launch the window of Visual Basic to run some code on your spreadsheets, you will need to enable the Developer tab on the excel Ribbon. You can do this easily opening the Excel options (File > Options) and searching for the Customize Ribbon tab, in this Tab you need to check the Developer checkbox to enable it in your regular interface:
Click on Ok and now you should be able to find the Developer tab on your excel ribbon. In this tab, launch the Visual Basic window:
In this new interface you will be able to run your VB code.
2. Building connection
In the Visual Basic window, open the code window of your sheet and let’s type some code! According to your needs you may create a custom macro and assign them to the action of buttons or other kind of stuff. In this example, we are going to work with plain code and will run them independently to test them. You need to understand how to connect to the workbook data source that will be handled with the following code:
Dim connection As Object
'--- Connect to the current datasource of the Excel file
Set connection = CreateObject("ADODB.Connection")
With connection
.Provider = "Microsoft.ACE.OLEDB.12.0"
.ConnectionString = "Data Source=" & ThisWorkbook.Path & "" & ThisWorkbook.Name & ";" & _
"Extended Properties=""Excel 12.0 Xml;HDR=NO"";"
.Open
End With
The connection properties are described as follows:
- Provider: we will use the Microsoft Access Database Engine 2010 (Microsoft.ACE.OLEDB.12.0)
- ConnectionString: we will use the current excel file as the database.
HDR=Yes;
: indicates that the first row contains the column names, not data.HDR=No;
indicates the opposite.
You will use this connection to run the SQL.
3. Printing whole table data
The following example, will use the mentioned logic to connect to the current spreadsheet and will query the range A1:E6
(selecting the whole table in the example excel) and will print every row in the immediate window:
Sub MyMethod()
'--- Declare Variables to store the connection, the result and the SQL query
Dim connection As Object, result As Object, sql As String, recordCount As Integer
'--- Connect to the current datasource of the Excel file
Set connection = CreateObject("ADODB.Connection")
With connection
.Provider = "Microsoft.ACE.OLEDB.12.0"
.ConnectionString = "Data Source=" & ThisWorkbook.Path & "" & ThisWorkbook.Name & ";" & _
"Extended Properties=""Excel 12.0 Xml;HDR=YES"";"
.Open
End With
'--- Write the SQL Query. In this case, we are going to select manually the data range
'--- To print the whole information of the table
sql = "SELECT * FROM [Sheet1$A1:E6]"
'--- Run the SQL query
Set result = connection.Execute(sql)
'--- Fetch information
Do
' Print the information of every column of the result
Debug.Print result(0); ";" & result(1) & ";" & result(2) & ";" & result(3) & ";" & result(4)
result.MoveNext
recordCount = recordCount + 1
Loop Until result.EOF
'--- Print the amount of results
Debug.Print vbNewLine & recordCount & " results found."
End Sub
Note that we are using HDR so the query will use the first row of data as the column headers, so the result will be the following one:
4. Query by columns
Now that you are able to connect to the worksheet, you may now customize the SQL to fit your needs. It is necessary to explain you the most basic thing you need to know about querying some data in your excel file. The range needs to specify the Sheet Name and the regular excel range (e.g. A1:Z1) and the whole data should be selected, not individual columns. You may filter by individual columns using regular SQL statements as WHERE, AND, OR, etc.
Depending if you use HDR (first row contains the column names), the query syntax will change:
HDR=YES
If you have HDR enabled (in the extended properties of the connection), you may query through the column name, considering that you selected the appropriate range:
SELECT * FROM [Sheet1$A1:E6] WHERE [city] = 'Boston'
HDR=NO
If you don’t use HDR, the nomenclature of the columns will follow the F1, F2, F3, …, FN pattern:
The following query would work perfectly if you don’t have HDR enabled (note that the range changes):
SELECT * FROM [Sheet1$A2:E6] WHERE [F5] = 'Boston'
In both cases, the output will be the same in the immediate window:
Jacob;1;boy;2010;Boston
Ethan;2;boy;2010;Boston
Michael;3;boy;2010;Boston
3 results found.
5. Answering questions
The SQL that should solve the initial questions will be the following ones (with HDR disabled):
Which users live in Boston.
SELECT * FROM [Sheet1$A2:E6] WHERE [F5] = 'Boston'
Which users are boys and live in Boston.
SELECT * FROM [Sheet1$A2:E6] WHERE [F5] = 'Boston' and [F3] = 'boy'
Which users were born in 2012.
SELECT * FROM [Sheet1$A2:E6] WHERE [F4] = 2012
Which users were born in 2010 and were ranked in place #1.
SELECT * FROM [Sheet1$A2:E6] WHERE [F2] = 1 AND [F4] = 2010
Happy coding ❤️!