Just wanted to add here that you can just make your range a named range and use it in your query (or an entire sheet if you have a single table per sheet).
So you can use:
SELECT * FROM MyNamedRange
OR
SELECT * FROM [Sheet1$]
A lot of these answers seem to go to the effort of parsing out addresses when what I’ve done historically is just setup all my tables as named ranges and refer to them directly in the query (makes it a lot cleaner too).
In one of my more complicated queries below, all of the ranges I refer to are named ranges:
SELECT Unit_Type AS [Unit Type], COUNT(*) AS [Number of Units], SQFT, Deposit AS [Deposit{$}], Rent AS [Base Rent{$}], SUM(RENT) AS [Base Rent Total{$}], SUM(MR) AS [Market Rent Total{$}] FROM
(
SELECT f1.Unit_Code, Unit_Type, SQFT, Rent, Deposit, SWITCH(MR IS NULL,Rent,MR IS NOT NULL,MR) AS MR FROM
(
SELECT t2.Unit_Code, t2.Unit_Type, SQFT, Rent, Deposit FROM
(
SELECT DISTINCT Unit_Code, Unit_Type
FROM CommUnits
WHERE Unit_Code NOT LIKE "%WAIT%" AND Exclude=0
) t2
LEFT JOIN
(
SELECT UnitType_Code, SQFT, Rent, Deposit
FROM ResUnitTypes
) t1 ON (t1.UnitType_Code = t2.Unit_Type)
) f1
LEFT JOIN
(
SELECT Unit_Code, SUM(Current_Charge) AS MR
FROM ResUnitAmenities
WHERE Unit_Code NOT LIKE "%WAIT%"
GROUP BY Unit_Code
) f2 on (f1.Unit_Code = f2.Unit_Code)
)
GROUP BY Unit_Type, SQFT, Deposit, Rent
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 ❤️!
Many times I was irritated of the lack of some Excel functionality (or just I don’t know there is) to easily transform data w/o using pivot tables. SQL in VBA was the only thing that was missing for me.
Distinct, grouping rows of Excel data, running multiple selects etc. Some time agon when I had a moment of time to spare I did my homework on the topic only to discover that running SQL queries from Excel VBA is possible and easy…
Want to create SQL Queries directly from Excel instead? See my Excel SQL AddIn
Want to learn how to create a MS Query manually? See my MS Query Tutorial
Using SQL in VBA example
Let see how to run a simple SELECT SQL Query in Excel VBA on an example Excel Worksheet. On the right see my Excel Worksheet and the Message Box with the similar output from my VBA Macro. The VBA Code is below:
Sub RunSELECT() Dim cn As Object, rs As Object, output As String, sql as String '---Connecting to the Data Source--- Set cn = CreateObject("ADODB.Connection") With cn .Provider = "Microsoft.ACE.OLEDB.12.0" .ConnectionString = "Data Source=" & ThisWorkbook.Path & "" & ThisWorkbook.Name & ";" & "Extended Properties=""Excel 12.0 Xml;HDR=YES"";" .Open End With '---Run the SQL SELECT Query--- sql = "SELECT * FROM [Sheet1$]" Set rs = cn.Execute(sql) Do output = output & rs(0) & ";" & rs(1) & ";" & rs(2) & vbNewLine Debug.Print rs(0); ";" & rs(1) & ";" & rs(2) rs.Movenext Loop Until rs.EOF MsgBox output '---Clean up--- rs.Close cn.Close Set cn = Nothing Set rs = Nothing End Sub
Explaining the Code
So what is happening in the macro above? Let us break it down:
Connecting to the Data Source
First we need to connect via the ADODB Driver to our Excel Worksheet. This is the same Driver which runs SQL Queries on MS Access Databases:
'---Connect to the Workbook--- Set cn = CreateObject("ADODB.Connection") With cn .Provider = "Microsoft.ACE.OLEDB.12.0" .ConnectionString = "Data Source=" & ThisWorkbook.Path & "" & ThisWorkbook.Name & ";" & _ "Extended Properties=""Excel 12.0 Xml;HDR=YES"";" .Open End With
The Provider is the Drive which is responsible for running the query.
The ConnectionStrings defines the Connection properties, like the path to the Queries File (example above is for ThisWorkbook) or if the first row contains a header (HDR).
The Open command executes the connection.
You can find more information on the ADODB.Connection Object on MSDN.
Running the SQL Select Query
Having connected to our Data Source Excel Worksheet we can now run a SQL SELECT Query:
'---Running the SQL Select Query--- Sql = "SELECT * FROM [Sheet1$]" Set rs = cn.Execute(Sql) Do output = output & rs(0) & ";" & rs(1) & ";" & rs(2) & vbNewLine Debug.Print rs(0); ";" & rs(1) & ";" & rs(2) rs.Movenext Loop Until rs.EOF MsgBox output
So what happens here? First we run the Execute command with our SELECT query:
SELECT * FROM [Sheet1$]
What does it do? It indicates that our records are in Sheet1. We can obviously extend this query just to filter people above the age of 30:
SELECT * FROM [Sheet1$] WHERE Age > 30
This would be the result:
The Execute command returns a ADODB RecordSet. We need to loop through the recordset to get each record:
Do '... 'Loop through records - rs(0) - first column, rs(1) - second column etc. '... rs.Movenext 'Move to next record Loop Until rs.EOF 'Have we reached End of RecordSet
Clean up
Lastly we need to Clean up our Objects to free memory. This is actually quite an important step as if you VBA code is runs a lot of queries or computations you might see a slow-down soon enough!
rs.Close cn.Close Set cn = Nothing Set rs = Nothing
What Else Can I Do?
You can do tons of great things with ADODB / MS Queries / SQL in Excel. Here are some additional ideas:
- Run Queries Across Worksheets – you can run JOIN queries on multiple Excel Worksheets. E.g.
SELECT [Sheet1$].[First Last], [Age], [Salary] FROM [Sheet1$] INNER JOIN [Sheet2$] ON [Sheet1$].[First Last]=[Sheet2$].[First Last]
On the below tables:
- Extracting Data from External Data Sources – use different connection strings to connect to Access Databases, CSV files or text files
- Do more efficient LOOKUPs – read my post on VLOOKUP vs SQL to learn more
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/
Приятного использования!
Представьте себе ситуацию, Вы получили целевую выборку из одной базы данных, но для полноты картины, как всегда, нужны дополнительные данные. Проблема может быть в том, что нужная информация хранится в другой базе данных и возможности создать на ней свою таблицу нет, подключиться используя link тоже нельзя, да и количество элементов, по которым нужно получить данные, несколько больше, чем допустимое на данном источнике. Вот и получается, что возможность написать SQL запрос и получить нужные данные есть, но написать придется не один запрос, а потом потратить время на объединение полученных данных.
Выйти из подобной ситуации поможет Excel.
Уверен, что ни для кого не секрет, что MS Excel имеет встроенный модуль VBA и надстройки, позволяющие подключаться к внешним источникам данных, то есть по сути является мощным инструментом для аналитики, а значит идеально подходит для решения подобных задач.
Для того чтобы обойти проблему, нам потребуется таблица с целевой выборкой, в которой содержатся идентификаторы, по которым можно достаточно корректно получить недостающую информацию (это может быть уникальный идентификатор, назовем его ID, или набор из данных, находящихся в разных столбцах), ПК с установленным MS Excel, и доступом к БД с недостающей информацией и, конечно, желание получить ту самую информацию.
Создаем в MS Excel книгу, на листе которой размещаем таблицу с идентификаторами, по которым будем в дальнейшем формировать запрос (если у нас есть уникальный идентификатор, для обеспечения максимальной скорости обработки таблицу лучше представить в виде одного столбца), сохраняем книгу в формате *.xlsm, после чего приступаем к созданию макроса.
Через меню «Разработчик» открываем встроенный VBA редактор и начинаем творить.
Sub job_sql() — Пусть наш макрос называется job_sql.
Пропишем переменные для подключения к БД, записи данных и запроса:
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sql As String
Опишем параметры подключения:
sql = «Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=True;Data Source=Storoge.company.ru Storoge.»
Объявим процедуру свойства, для присвоения значения:
Set cn = New ADODB.Connection
cn.Provider = » SQLOLEDB.1″
cn.ConnectionString = sql
cn.ConnectionTimeout = 0
cn.Open
Вот теперь можно приступать непосредственно к делу.
Организуем цикл:
For i = 2 To 1000
Как вы уже поняли конечное значение i=1000 здесь только для примера, а в реальности конечное значение соответствует количеству строк в Вашей таблице. В целях унификации можно использовать автоматический способ подсчета количества строк, например, вот такую конструкцию:
Dim LastRow As Long
LastRow = ActiveSheet.UsedRange.Row — 1 + ActiveSheet.UsedRange.Rows.Count
Тогда открытие цикла будет выглядеть так:
For i = 2 To LastRow
Как я уже говорил выше MS Excel является мощным инструментом для аналитики, и возможности Excel VBA не заканчиваются на простом переборе значений или комбинаций значений. При наличии известных Вам закономерностей можно ограничить объем выгружаемой из БД информации путем добавления в макрос простых условий, например:
If Cells(i, 2) = «Ваше условие» Then
Итак, мы определились с объемом и условиями выборки, организовали подключение к БД и готовы формировать запрос. Предположим, что нам нужно получить информацию о размере ежемесячного платежа [Ежемесячный платеж] из таблицы [payments].[refinans_credit], но только по тем случаям, когда размер ежемесячного платежа больше 0
sql = «select [Ежемесячный платеж] from [PAYMENTS].[refinans_credit] » & _
«where [Ежемесячный платеж]>0 and [Номер заявки] ='» & Cells(i, 1) & «‘ «
Если значений для формирования запроса несколько, соответственно прописываем их в запросе:
«where [Ежемесячный платеж]>0 and [Номер заявки] = ‘» & Cells(i, 1) & «‘ » & _
» and [Дата платежа]='» & Cells(i, 2) & «‘»
В целях самоконтроля я обычно записываю сформированный макросом запрос, чтобы иметь возможность проверить его корректность и работоспособность, для этого добавим вот такую строчку:
Cells(i, 3) = sql
в третьем столбце записываются запросы.
Выполняем SQL запрос:
Set rs = cn.Execute(sql)
А чтобы хоть как-то наблюдать за выполнением макроса выведем изменение i в статус-бар
Application.StatusBar = «Execute script …» & i
Application.ScreenUpdating = False
Теперь нам нужно записать полученные результаты. Для этого будем использовать оператор Do While:
j = 0
Do While Not rs.EOF
For ii = 0 To rs.Fields.Count — 1
Cells(i, 4 + j + ii) = rs.Fields(0 + ii) ‘& «;»
Указываем ячейки для вставки полученных данных (4 в примере это номер столбца с которого начинаем запись результатов)
Next ii
j = j + rs.Fields.Count
s.MoveNext
Loop
rs.Close
End If
— закрываем цикл If, если вводили дополнительные условия
Next i
cn.Close
Application.StatusBar = «Готово»
End Sub
— закрываем макрос.
В дополнение хочу отметить, что данный макрос позволяет обращаться как к БД на MS SQL так и к БД Oracle, разница будет только в параметрах подключения и собственно в синтаксисе SQL запроса.
В приведенном примере для авторизации при подключении к БД используется доменная аутентификация.
А как быть если для аутентификации необходимо ввести логин и пароль? Ничего невозможного нет. Изменим часть макроса, которая отвечает за подключение к БД следующим образом:
sql = «Provider= SQLOLEDB.1;Password=********;User ID=********;Data Source= Storoge.company.ru Storoge;APP=SFM»
Но в этом случае при использовании макроса возникает риск компрометации Ваших учетных данных. Поэтому лучше программно удалять учетные данные после выполнения макроса. Разместим поля для ввода пароля и логина на листе и изменим макрос следующим образом:
sql = «Provider= SQLOLEDB.1;Password=» & Sheets(«Лист аутентификации»).TextBox1.Value & «;User ID=» & Sheets(«Лист аутентификации «).TextBox2.Value & «;Data Source= Storoge.company.ru Storoge;APP=SFM»
Место для расположения текстовых полей не принципиально, можно расположить их на листе с таблицей в первых строках, но мне удобней размещать поля на отдельном листе. Чтобы введенные учетные данные не сохранялись вместе с результатом выполнения макроса в конце исполняемого кода дописываем:
Sheets(«Выгрузка»).TextBox1.Value = «« Sheets(»Выгрузка«).TextBox2.Value = »»
То есть просто присваиваем текстовым полям пустые значения, таким образом после выполнения макроса поля для ввода пароля и логина окажутся пустыми.
Вот такое вполне жизнеспособное решение, позволяющее сократить трудозатраты при получении и обработке данных, я использую. Надеюсь мой опыт применения SQL запросов в Excel будет полезен и вам в решении текущих задач.
Vitallic Пользователь Сообщений: 239 |
Добрый день. Подскажите как решить такую проблему: есть excel-файл в котором находяться нужные (но избыточные) данные. Прикрепленные файлы
|
ikki Пользователь Сообщений: 9709 |
#2 11.02.2014 18:31:28 можно и 50 а можно так
сам запрос:
(обратите внимание на одинарные кавычки — синтаксисом допускается) фрилансер Excel, VBA — контакты в профиле |
||||
Vitallic Пользователь Сообщений: 239 |
#3 11.02.2014 18:50:05 ikki, спасибо (особенно за одинарные кавычки в свое время с кавычками немало поломал голову).
ну и по понятным причинам ничего не вытаскивает из «базы». |
||
ikki Пользователь Сообщений: 9709 |
#4 11.02.2014 19:04:05 извините, ошибся — у меня старый Excel, а переписывать весь код для проверки не хотелось
или так, с использованием функции VBA
Изменено: ikki — 11.02.2014 19:13:48 фрилансер Excel, VBA — контакты в профиле |
||||
Vitallic Пользователь Сообщений: 239 |
Спасибо, буду разбираться |
ikki Пользователь Сообщений: 9709 |
2003-й. пс. ещё раз скопируйте код — опять наколбасил с кавычками Изменено: ikki — 11.02.2014 19:14:51 фрилансер Excel, VBA — контакты в профиле |
Vitallic Пользователь Сообщений: 239 |
Знал что не работало еще когдпа писал предыдущее сообщение |
ikki Пользователь Сообщений: 9709 |
так и пришлось попробовать… итого: оба способа рабочие Изменено: ikki — 11.02.2014 23:33:06 фрилансер Excel, VBA — контакты в профиле |
ikki Пользователь Сообщений: 9709 |
пс. само собой — без ADO (на массивах и словаре) макрос будет работать быстрее. фрилансер Excel, VBA — контакты в профиле |
anvg Пользователь Сообщений: 11878 Excel 2016, 365 |
Доброе время суток. Прикрепленные файлы
|
Vitallic Пользователь Сообщений: 239 |
ikki, таки да читал где-то на форуме что быстрее на словарях-массивах, но важно было именно с помощью ADODB |
anvg Пользователь Сообщений: 11878 Excel 2016, 365 |
#12 12.02.2014 11:19:29
Судя по некоторым особенностям именования — не исключено . При запросах к таблицам Excel используется движок Access (с некоторыми особенностями обращения к таблицам на листе [ИмяЛиста$]), так что имеет смысл поискать книжки по его диалекту SQL. Следует также учитывать то, что типы данных проверяются по первым 8 строкам (по-умолчанию) таблицы, так что желательно в них иметь заполненные данные для правильной работы. |
||
Vitallic Пользователь Сообщений: 239 |
anvg
, еще раз спасибо. |
PowerBoy Пользователь Сообщений: 141 |
Можете глянуть мою надстройку, пока еще бета версия. Но данные вытянет откуда хотите. http://files.mail.ru/20A7C9975D374B4FAB199D1148831349
Изменено: PowerBoy — 12.02.2014 11:31:52 Excel + SQL = Activetables |
ikki Пользователь Сообщений: 9709 |
в офисе 97 была совершенно шикарная русская справка по ядру Microsoft Jet раздел так и называется — «Справочник Microsoft Jet SQL» фрилансер Excel, VBA — контакты в профиле |
ikki Пользователь Сообщений: 9709 |
зы. PowerBoy, а Вам не кажется, что Вы подобными постами (я их не первый раз вижу), нарушаете п.3.5 Правил форума? Изменено: ikki — 12.02.2014 12:23:25 фрилансер Excel, VBA — контакты в профиле |
PowerBoy Пользователь Сообщений: 141 |
#17 12.02.2014 12:25:52
Не знаю — разъясните что именно? Надстройка у меня бесплатная. К данной теме имеет прямое отношение. Excel + SQL = Activetables |
||
ikki Пользователь Сообщений: 9709 |
#18 12.02.2014 12:38:04
«любые» в моём понимании — это любые. и дискутировать тут не о чем. впрочем, в Правилах есть ещё п.5.3. фрилансер Excel, VBA — контакты в профиле |
||
The_Prist Пользователь Сообщений: 14182 Профессиональная разработка приложений для MS Office |
ikki, ну это перебор. Надстройка имеет отношение к теме, я лично не вижу ничего плохого в этом. Притом тут даже не столько реклама, сколько файл. С таким же успехом можно тогда запретить и файлы с решениями выкладывать и полезными решениями. Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы… |
ikki Пользователь Сообщений: 9709 |
ок. вопросов больше нет. фрилансер Excel, VBA — контакты в профиле |
Vitallic Пользователь Сообщений: 239 |
а я думал тема исчерпана ikki , конечно спасибо за еще один источник информации, PowerBoy , к сожалению на работе (а я именно что на роботе) екзешник посмотреть не смогу что же касается моей задачи то мне как раз и нужен «чистый» код поскольку я делаю какбы готовое решение не для себя (для сотрудников) |
ikki Пользователь Сообщений: 9709 |
#22 12.02.2014 17:03:29
в 97-м году другого не было фрилансер Excel, VBA — контакты в профиле |
||
Tikr Пользователь Сообщений: 69 Tikr |
Всем привет |
anvg Пользователь Сообщений: 11878 Excel 2016, 365 |
#24 21.02.2014 02:50:43 Доброе время суток
запрос для Recordset.Open будет выглядеть приблизительно так
Естественно сам SQL запрос по требуемому смыслу. Успехов. |
||||
Tikr Пользователь Сообщений: 69 Tikr |
#25 21.02.2014 12:29:38 Спасибо за ответ
‘ Run time erroк 3021’ BOF или EOF имеет значение True, либо текущая запись удалена. Для выполнения операции требуется текущая операция |
||
Tikr Пользователь Сообщений: 69 Tikr |
Добрый день, уважаемые коллеги!) |
PowerBoy Пользователь Сообщений: 141 |
#27 21.03.2014 14:05:16
Excel + SQL = Activetables |
||
Tikr Пользователь Сообщений: 69 Tikr |
Добрый день! |
PowerBoy Пользователь Сообщений: 141 |
#29 23.05.2014 08:49:29
Вы запрос свой покажите, то не очень понятно. Excel + SQL = Activetables |
||
Tikr Пользователь Сообщений: 69 Tikr |
#30 23.05.2014 09:15:20 Взял небольшой кусочек из основной портянки
|
||
Работа с внешними источниками данных
Материалы по работе с внешними источниками данных на примере 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
07 Aug 3 Ways to Perform an Excel SQL Query
Posted at 12:08h
in Excel VBA
0 Comments
Howdee! Excel is a great tool for performing data analysis. However, sometimes getting the data we need into Excel can be cumbersome and take a lot of time when going through other systems. You’re also at the mercy of how a disparate system exports data, and may need an additional step between exporting and getting the data into the format you need. If you have access to the database where the data is housed, you can circumvent these steps and create your own custom Excel SQL query.
To follow along with my below demos, you’ll need to have an instance of SQL server installed on your desktop. If you don’t, you can download the trial version, developer version, or free express version here. I’ll be working with the free developer version in this article. I’m also using a sample database that you can download here. The easiest way to install this is using SQL Server Management Studio (SSMS). That download is available here. Once you open SSMS, it should automatically detect your local server instance. You must ensure your SQL Server User is running as the “Local Client” and then you can create a blank database, and restore that database from the backup file. If you have issues accomplishing this, let me know in the comments and I’ll elaborate on how this is done.
If you are familiar enough with SQL and have access to your own data, you can skip these steps and use your data. Otherwise, I recommend downloading these tools before getting started. If you’re new to SQL, I highly recommend the SQL Essential Training courses on Lynda.com. Now, on to why you’re all here…
Excel SQL Query Using Get Data
This option is the most straight forward approach to creating an Excel SQL query. However, it is important to note that this approach is only available in Excel 2013 and later and will not currently work on Mac OSX. To get started, select “Get Data” à “From Database” à “From SQL Server Database” as shown in the screen grab. At this point it will pop-up a prompt to enter your server name and the target database you’re wanting to query (you can get this information from SSMS). You can enter this information and then select “OK”. This will allow you to browse available tables from that database to import. You can remove columns and filter tables before importing. If you do not know how to write SQL queries yet, this is one approach you can take.
However, if you select the “Advanced” dropdown arrow, you can create your own custom Excel SQL query. I usually create my query in SSMS or Visual Studio and then just paste the final query in this window. That is because there is no intellisense in this window and it can be difficult to spot errors in your query. Once you select OK, it will ask you to confirm credentials and you may get an error about encryption. This is common when connecting to databases in this manner and nothing to worry about. The next screen will provide an example of your data and you can select “Load” to import it.
This will create a table on a new tab and you’ll also notice a new pane on the right titled “Connections & Queries”. It will display the name of your query (defaults to “Query1”, “Query2”, etc.) and you can rename the query by right-clicking and selecting “Rename”. You can also edit the query from this location as well. It will open up an interface with a sample of your data and you can add/remove columns, filter your data, or edit your source query from here.
Now that you’ve set up this Excel SQL query, you can simply refresh the data set with fresh data anytime by clicking “Refresh All” on the “Data” ribbon. A quick side note here. If you pivot this data, “Refresh All” will refresh pivot tables first and then the query. To update your pivot table, you’ll need to refresh all twice or update your pivot table manually. To me, one of the downsides of this approach is the results are always returned in a table. I personally do not like working with tables in Excel. That’s where using VBA for your SQL query can come in handy.
Excel SQL Query Using VBA
Using VBA to create your Excel SQL query is not as straight forward as the previous approach, but can still be an extremely useful method depending on your situation. I particularly like that the data is not returned to a table unless you designate it to be so. This technique will work on older versions of Microsoft Excel but will not work on Mac OSX versions of Excel since it uses and ADO connection.
To get started, open up the VBA editor by pressing alt+F11. Before beginning to write your code, you’ll need to ensure that the “Microsoft ActiveX Data Objects 2.0 Library” is referenced from the VBA Project. To do this, click on “Tools” in the ribbon menu at the top of the VBA editor. In the popup, ensure the library is checked as shown below. This allows the project to use the ADO connectors to create the connection to your database. Next, let’s dimension a few variables.
Dim Conn As New ADODB.Connection
Dim recset As New ADODB.Recordset
Dim sqlQry As String, sConnect As String
The Conn variable is will be used to represent the connection between our VBA project and the SQL database. The receset variable will represent a new record set through which we will give the command to perform our Excel SQL query using the connection we’ve established. Finally, the sqlQry variable will represent a string variable that is our SQL query command, and the sConnect variable will be a string representing the connection string the database requires. Let’s look at how to use these variables to perform a SQL query.
sqlQry = "select top 1000 si.InvoiceID, si.InvoiceDate, sc.CustomerName from Sales.Invoices si" & _
" left join sales.Customers sc on sc.CustomerID = si.CustomerID"
sConnect = "Driver={SQL Server};Server=[Your Server Name Here]; Database=[Your Database Here];Trusted_Connection=yes;"
Conn.Open sConnect
Set recset = New ADODB.Recordset
recset.Open sqlQry, Conn
Sheet2.Cells(2, 1).CopyFromRecordset recset
recset.Close
Conn.Close
Set recset = Nothing
While this may look complex, each step is relatively simple. Firstly, we set our sqlQry variable equal to a string that represents the syntax of our SQL query. We then create a connection string we can use in our next command to connect to the database. So, “Conn.Open” is the command to open the connection and “sConnect” is the string it uses to do so. “Trusted_Connection=yes” means that the connection will attempt to be established using your Microsoft credentials for the account you’re logged in as.
Now that the connection is open, we can open a new record set and pass it the sql command using the sqlQry variable, and tell it which connection to use by passing it the Conn variable. We can then use the VBA command “CopyFromRecordset” to paste the recordset anywhere in our workbook. It’s important to close both the record set and connection at this point. You also want to set your recset variable equal to nothing so it does not eat up valuable resources.
One of the downsides to using this method is that you must explicitly tell Excel some things that the previous approach did automatically. For example, this SQL query will not return any column headers. Therefore, you must explicitly tell Excel what to label your columns. Secondly, the data is not automatically cleared and the new query imported. You must also explicitly tell Excel to do this as well. Here is the final code with those commands added.
Sub SQL_Example()
Dim Conn As New ADODB.Connection
Dim recset As New ADODB.Recordset
Dim sqlQry As String, sConnect As String
Sheet2.Cells.ClearContents
sqlQry = "select top 1000 si.InvoiceID, si.InvoiceDate, sc.CustomerName from Sales.Invoices si" & _
" left join sales.Customers sc on sc.CustomerID = si.CustomerID"
sConnect = "Driver={SQL Server};Server=[Your Server Name Here]; Database=[Your Database Name Here];Trusted_Connection=yes;"
Conn.Open sConnect
Set recset = New ADODB.Recordset
recset.Open sqlQry, Conn
Sheet2.Cells(2, 1).CopyFromRecordset recset
recset.Close
Conn.Close
Set recset = Nothing
Sheet2.Cells(1, 1) = "Invoice ID"
Sheet2.Cells(1, 2) = "Invoice Date"
Sheet2.Cells(1, 3) = "Customer Name"
End Sub
My preference for using this approach is when I want the user to be able to pass parameters to my Excel SQL query. For example, I might have a dropdown of customer names the user could select. By using this tactic, I can easily add a dropdown of customer names the user can select, and pass that value to my SQL query in a where clause.
As you can see, both the built in Excel SQL query and the VBA method have pros and cons. I employ both in my everyday work depending on what situation I find myself in.
Excel SQL Query Using Microsoft Query
This option is likely the most complex option, but it has the added advantage of being compatible with some versions of Mac OSX. I won’t pretend to be an expert at creating Mac OSX compatible tools for Excel, but I have successfully used this implementation to create an embedded Excel SQL query for Macs in the past.
I also like this method because you can create popup style parameters. For example, you can prompt the user to input date range parameters at the time the SQL query is ran. Like the first example, running this query is as easy as clicking “Refresh All” on the Data ribbon. Let’s dive in to the details.
To get started here, click “Get Data” on the Data ribbon. In the menu that dropdowns select “From Other Sources” and, finally “From Microsoft Query”.
This will open a wizard for you to choose your data source. Double click “<New Data Source>” and you’ll be prompted to enter some information about your data source. Option 1 can be anything you wish that describes your data source. Option 2 should be “SQL Server”. Click “Connect” and it will pop up a third window where you can enter information about the server and login information. Be sure you select the “Options>>” dropdown so you can select the database you’re wanting to connect to.
You’ll now have a new data source in the original window. Double click the data source to bring up a table import wizard. If you want to import an entire table, you can do so here and even filter and sort the data using the import wizard. However, if you want to use your own custom query as we have been, just select any field and go through the wizard and import the data. When you come to screen that asks you if you want to return the data to Excel or edit in a query, return the data to Excel. It will then prompt you to select where you want the data returned in your workbook.
The query will return the data in a table format. To change it to your own custom SQL query, let’s follow these steps:
- Click anywhere in the data table.
- On the Excel Data Ribbon, in the “Queries & Connections” group, properties will no longer be grayed out like it normally is. Click this.
- In the popup – you’ll see another properties icon. Click this.
- In this popup, select the “Definition” tab and paste your SQL query in the “Command Text” input box.
Now you’ve built an Excel SQL Query that can be refreshed anytime the workbook is refreshed. In my screengrab, the “Parameters” button is greyed out. If you want to add parameters to your query, you do so by adding “?” in your command text. That looks like this.
This creates a parameter the end user can interact with. You can have the user be prompted to enter an input when the workbook is refreshed, select a default value, or have it linked to a cell in the workbook. Even though this option is cumbersome to set up, I really enjoy using it. It allows me a lot of flexibility to have the user interact with the data. As I touched on in the beginning, I’ve had success using this option on Microsoft Office for Mac OSX. I don’t want to say this will work 100% of the time on a Mac because I’ve also had it fail. If anyone has any input on this, I’d love to hear from you.
Let me know your thoughts on these approaches in the comments! What other ways do you creatively get data into Excel from SQL data sources?
Cheers!
R