You are on page 1of 45

Computational Finance and Risk Management

Financial Data Access with SQL, Excel & VBA

Guy Yollin
Instructor, Applied Mathematics University of Washington
Guy Yollin (Copyright 2012) Data Access with SQL, Excel & VBA Data access with VBA 1 / 45

Outline

Introduction to QueryTables Downloading equity data from Yahoo Finance Read data from SQL databases Implementing VBA worksheet formulas VBA chart creation

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

2 / 45

Outline

Introduction to QueryTables Downloading equity data from Yahoo Finance Read data from SQL databases Implementing VBA worksheet formulas VBA chart creation

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

3 / 45

Section references

J. Green, S. Bullen, R. Bovey, M. Alexander Excel 2007 VBA Programmers Reference Wiley, 2007 Chapter 21

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

4 / 45

The QueryTables collection

The QueryTables object is a collection of QueryTable objects Use the Add method to create a new query table and add it to the QueryTables collection Each QueryTable object represents a worksheet table built from data returned from an external data source

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

5 / 45

The QueryTables.Add Method


The QueryTables.Add Method creates a new query table Syntax: expression .Add(Connection, Destination, Sql) Connection The data source for the query table "ODBC;<connection string>" "URL;<url>" "TEXT;<text le path and name>" The cell in the upper-left corner of the query table destination range An optional SQL query string for an ODBC data source

Destination Sql

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

6 / 45

The QueryTable.Refresh Method


The Refresh method causes Excel to connect to the data source of the QueryTable object, execute the SQL query, and return data to the range that is based on the QueryTable object Syntax: expression.Refresh(BackgroundQuery) BackgroundQuery Used only with QueryTables that are based on the results of a SQL query True to return control to the procedure as soon as a database connection is made and the query is submitted False to return control to the procedure only after all data has been fetched to the worksheet

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

7 / 45

Properties and methods of the QueryTable object


Properties BackgroundQuery CommandText CommandType TextFileParseType TextFileCommaDelimiter Methods Refresh
Guy Yollin (Copyright 2012)

True if queries for the query table are performed asynchronously (in the background) Returns or sets the command string for the specied data source The CommandType describes the value of the CommandText property Gets/sets text le column format True if the comma is the delimiter when you import a text le

Updates an external data range (QueryTable)


Data Access with SQL, Excel & VBA Data access with VBA 8 / 45

see QueryTable Obect (Excel) http://msdn.microsoft.com/en-us/library/office/ff198271.aspx

Steps to retrieve data from an external data source

Most data fetches will follow this basic procedure:


1 2 3 4 5

Dene the connection string Dene the destination Call QueryTables.Add to create the query table object Set the command (i.e. specify the data to be retrieved) Call QueryTable.Refresh to get the data

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

9 / 45

Outline

Introduction to QueryTables Downloading equity data from Yahoo Finance Read data from SQL databases Implementing VBA worksheet formulas VBA chart creation

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

10 / 45

Section references

Simon Beginninga Financial Modeling, 3rd Edition Massachusetts Institute of Technology, 2008 Chapter 41 J. Green, S. Bullen, R. Bovey, M. Alexander Excel 2007 VBA Programmers Reference Wiley, 2007 Chapter 21

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

11 / 45

Browser initiated download


It is extremely easy to download historic equity data from Yahoo Finance directly into Excel Just enter the following URL into a broswer: http://ichart.yahoo.com/table.csv?s=^GSPC
the parameter "s=" species the Yahoo ticker symbol

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

12 / 45

Browser initiated download


Since only the ticker symbol and no date range was specied, all available data is downloaded

S&P 500 Index from Jan-1950 to most recent trading day


Guy Yollin (Copyright 2012) Data Access with SQL, Excel & VBA Data access with VBA 13 / 45

Full Yahoo nance download string


Download S&P 500 index for 2011:
http://ichart.yahoo.com/table.csv?s=^GSPC&a=0&b=01&c=2011&d=11&e=31&f=2011&g=d

Parameter s a b c d e f g
Guy Yollin (Copyright 2012)

Description ticker symbol fromMonth - 1 fromdat (2 digits) fromYear toMonth - 1 toDay (2 digits) toYear d=day, m=month

Data Access with SQL, Excel & VBA

Data access with VBA

14 / 45

VBA code to download Yahoo data


Sub G e t P r i c e s V e r 1 ( ) Dim Ws As W o r k s h e e t Dim Wq As Q u e r y T a b l e Dim U r l As S t r i n g Url = _ "URL ; " & _ " h t t p : / / i c h a r t . f i n a n c e . yahoo . com/ t a b l e . c s v ? " & _ " s=^GSPC&" & _ " g=d&" & _ " i g n o r e =. c s v " S e t Ws = A c t i v e S h e e t S e t Wq = Ws . Q u e r y T a b l e s . Add ( _ C o n n e c t i o n := U r l , _ D e s t i n a t i o n :=Ws . Range ( "A1" ) ) Wq. R e f r e s h BackgroundQuery := F a l s e End Sub

adapted from S. Beginninga, Financial Modeling, 3rd Ed.


Guy Yollin (Copyright 2012) Data Access with SQL, Excel & VBA Data access with VBA 15 / 45

Unformatted yahoo download results


The VBA query inserts the CSV data as one-cell-per-row

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

16 / 45

VBA code to download Yahoo data


Sub G e t P r i c e s V e r 2 ( ) Dim Ws As W o r k s h e e t Dim Wq As Q u e r y T a b l e Dim U r l As S t r i n g Url = _ "TEXT ; " & _ " h t t p : / / i c h a r t . f i n a n c e . yahoo . com/ t a b l e . c s v ? " & _ " s=^GSPC&" & _ " g=d&" & _ " i g n o r e =. c s v " S e t Ws = A c t i v e S h e e t S e t Wq = Ws . Q u e r y T a b l e s . Add ( _ C o n n e c t i o n := U r l , _ D e s t i n a t i o n :=Ws . Range ( "A1" ) ) Wq. T e x t F i l e P a r s e T y p e = x l D e l i m i t e d Wq. T e x t F i l e C o m m a D e l i m i t e r = True Wq. R e f r e s h BackgroundQuery := F a l s e End Sub adapted from S. Beginninga, Financial Modeling, 3rd Ed.
Guy Yollin (Copyright 2012) Data Access with SQL, Excel & VBA Data access with VBA 17 / 45

Properly formatted yahoo download results

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

18 / 45

Outline

Introduction to QueryTables Downloading equity data from Yahoo Finance Read data from SQL databases Implementing VBA worksheet formulas VBA chart creation

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

19 / 45

Section references

J. Green, S. Bullen, R. Bovey, M. Alexander Excel 2007 VBA Programmers Reference Wiley, 2007 Chapter 21

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

20 / 45

Data access plan

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

21 / 45

ODBC Setup

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

22 / 45

Read from ODBC Access database


Sub g o a t H i t t e r s ( ) Dim varC on n As S t r i n g Dim varSQL As S t r i n g Range ( "A1" ) . C u r r e n t R e g i o n . C l e a r C o n t e n t s varConn = "ODBC; DSN=lahman " varSQL = _ "SELECT " & _ " M a s t e r . n a m e F i r s t , M a s t e r . nameLast , SUM(H) AS H i t s " & _ "FROM B a t t i n g , M a s t e r " & _ "WHERE M a s t e r . p l a y e r I D=B a t t i n g . p l a y e r I D " & _ "GROUP BY M a s t e r . p l a y e r I D , M a s t e r . nameLast , M a s t e r . n a m e F i r s t " & _ "HAVING SUM(H) > 3000 " & _ "ORDER BY SUM(H) DESC ; " With A c t i v e S h e e t . Q u e r y T a b l e s . Add ( _ C o n n e c t i o n := varConn , _ D e s t i n a t i o n := Range ( "A1" ) ) . CommandText = varSQL . R e f r e s h B a c k g r o u n d Q u e r y := F a l s e End With
Guy Yollin (Copyright 2012) Data Access with SQL, Excel & VBA Data access with VBA 23 / 45

Read from ODBC Access database


Sub goatHomers ( ) Dim va rConn As S t r i n g Dim varSQL As S t r i n g Cells . Clear varConn = "ODBC;DBQ=C : \ P r o j e c t s \VBA\ lahman591 . mdb ; D r i v e r ={ M i c r o s o f t A c c e s s D r i v e r ( . mdb ) } " varSQL = _ "SELECT " & _ " n a m e F i r s t , nameLast , y e a r I D , HR " & _ "FROM B a t t i n g , M a s t e r " & _ "WHERE M a s t e r . p l a y e r I D=B a t t i n g . p l a y e r I D " & _ "AND HR>51 " & _ "ORDER BY HR DESC ; " With A c t i v e S h e e t . Q u e r y T a b l e s . Add ( C o n n e c t i o n := varConn , D e s t i n a t i o n := Range ( "A1" ) ) . CommandText = varSQL . R e f r e s h B a c k g r o u n d Q u e r y := F a l s e End With End Sub

Note DSN-less ODBC connection

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

24 / 45

Read from SQLite database via ODBC


Sub g o a t S t e a l e r s ( ) Dim varC on n As S t r i n g Dim varSQL As S t r i n g Cells . Clear varConn = "ODBC; DSN=l a h m a n S Q L i t e " varSQL = _ "SELECT " & _ " M a s t e r . n a m e F i r s t , M a s t e r . nameLast , SUM( SB ) AS S t o l e n B a s e s " & _ "FROM B a t t i n g , M a s t e r " & _ "WHERE M a s t e r . p l a y e r I D=B a t t i n g . p l a y e r I D " & _ "GROUP BY M a s t e r . p l a y e r I D " & _ "HAVING SUM( SB ) > 600 " & _ "ORDER BY SUM( SB ) DESC ; " With A c t i v e S h e e t . Q u e r y T a b l e s . Add ( _ C o n n e c t i o n := varConn , _ D e s t i n a t i o n := Range ( "A1" ) ) . CommandText = varSQL . R e f r e s h B a c k g r o u n d Q u e r y := F a l s e End With
Guy Yollin End Sub (Copyright 2012) Data Access with SQL, Excel & VBA Data access with VBA 25 / 45

Read from Access database via ADO


Sub goatHomersADO ( ) R e q u i r e s r e f e r e n c e t o M i c r o s o f t A c t i v e X Data O b j e c t s xx L i b r a r y Dim Cn As ADODB. C o n n e c t i o n , Rs As ADODB. R e c o r d s e t Dim MyConn , varSQL As S t r i n g Dim Rw As Long , C o l As Long , c As Long Dim M y F i e l d , L o c a t i o n As Range Cells . Select Selection . Clear Range ( "A1" ) . S e l e c t Set d e s t i n a t i o n S e t L o c a t i o n = [ A1 ] Set source MyConn = "C : \ P r o j e c t s \VBA\ lahman591 . mdb" Create query varSQL = _ "SELECT " & _ " M a s t e r . n a m e F i r s t , M a s t e r . nameLast , SUM(HR) AS HomeRuns " & _ "FROM B a t t i n g , M a s t e r " & _ "WHERE M a s t e r . p l a y e r I D=B a t t i n g . p l a y e r I D " & _ "GROUP BY M a s t e r . p l a y e r I D , M a s t e r . n a m e F i r s t , M a s t e r . nameLast " & _ "ORDER BY SUM(HR) DESC ; " ...

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

26 / 45

Read from Access database via ADO


... Create RecordSet S e t Cn = New ADODB. C o n n e c t i o n With Cn . P r o v i d e r = " M i c r o s o f t . J e t . OLEDB . 4 . 0 " . Open MyConn S e t Rs = . E x e c u t e ( varSQL ) End With Write RecordSet to r e s u l t s area Rw = L o c a t i o n . Row C o l = L o c a t i o n . Column c = Col Do U n t i l Rs . EOF F o r Each M y F i e l d I n Rs . F i e l d s C e l l s (Rw , c ) = M y F i e l d c = c + 1 Next M y F i e l d Rs . MoveNext Rw = Rw + 1 c = Col Loop Set Location = Nothing S e t Cn = N o t h i n g End Sub

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

27 / 45

Outline

Introduction to QueryTables Downloading equity data from Yahoo Finance Read data from SQL databases Implementing VBA worksheet formulas VBA chart creation

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

28 / 45

Section references

John Walkenbach Excel 2010 Power Programming with VBA Sams, 2010 Chapter 8

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

29 / 45

Black-Scholes pricing formulas


Price of a call option: c = S0 N (d1 ) Xe rT N (d2 ) Price of a put option: p = Xe rT N (d2 ) S0 N (d1 ) Where:
0 ln( S r + 2 /2)T X ) + ( T d2 = d1 T

S0 = spot price X = strike price = volatility T = time to expiration r = risk-free rate

d1 =

N The cumulative normal distribution function

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

30 / 45

Black-Shoales formula
P u b l i c F u n c t i o n B l a c k S c h o l e s ( C a l l P u t F l a g As S t r i n g , _ S As Double , X As Double , T As Double , r As Double , _ v As Double ) As Double Dim d1 As Double , d2 As Double d1 = ( Log ( S / X) + ( r + v ^ 2 / 2 ) T) / ( v Sqr (T ) ) d2 = d1 v Sq r (T) I f CallPutFlag = "c" BlackScholes = S E l s e I f CallPutFlag = BlackScholes = X End I f End F u n c t i o n Then CND( d1 ) X Exp( r T) CND( d2 ) " p " Then Exp( r T) CND( d2 ) S CND( d1 )

Source: http://www.espenhaug.com/black_scholes.html
Guy Yollin (Copyright 2012) Data Access with SQL, Excel & VBA Data access with VBA 31 / 45

Polynomial approximation to normal CDF

N (x ) = where

1 N (x )(a1 k + a2 k 2 + a3 k 3 + a4 k 4 + a5 k 5 ) when x 0 1 N (x ) when x < 0

k=

1 1+ x

= 0.2316419 a1 = 0.319381530 a2 = 0.356563782 a3 = 1.781477937 a4 = 1.821255978 a5 = 1.330274429


Guy Yollin (Copyright 2012) Data Access with SQL, Excel & VBA Data access with VBA 32 / 45

Black-Shoales formula
// The c u m u l a t i v e n o r m a l d i s t r i b u t i o n f u n c t i o n P u b l i c F u n c t i o n CND(X As Double ) As Double Dim L Const Const Const As a1 a3 a5 Double , K As Double = 0 . 3 1 9 3 8 1 5 3 : C o n s t a2 = 0. 35 65 6 3 78 2: = 1 . 7 8 1 4 7 7 9 3 7 : C o n s t a4 = 1. 82 12 55 97 8: = 1.330274429

L = Abs (X) K = 1 / (1 + 0.2316419 L) CND = 1 1 / Sqr ( 2 A p p l i c a t i o n . P i ( ) ) Exp( L ^2/2) _ ( a1 K + a2 K^2 + a3 K^3 + a4 K^4 + a5 K^5) I f X < 0 Then CND = 1 CND End I f End F u n c t i o n

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

33 / 45

Black-Shoales option pricing

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

34 / 45

Calling worksheet functions from VBA

You can use many (but not all) of Excels worksheet functions in your VBA code The WorksheetFunction object, which is contained in the Application object, holds all the worksheet functions that you can call from your VBA procedures To use a worksheet function in a VBA statement, just precede the function name with the object reference:
Application.WorksheetFunction

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

35 / 45

Black-Shoales formula
P u b l i c F u n c t i o n B l a c k S c h o l e s V e r 2 ( C a l l P u t F l a g As S t r i n g , _ S As Double , X As Double , T As Double , r As Double , _ v As Double ) As Double Dim d1 As Double , d2 As Double d1 = ( Log ( S / X) + ( r + v ^ 2 / 2 ) T) / ( v Sqr (T ) ) d2 = d1 v Sq r (T) I f C a l l P u t F l a g = " c " Then B l a c k S c h o l e s V e r 2 = S W o r k s h e e t F u n c t i o n . NormSDist ( d1 ) _ X Exp( r T) W o r k s h e e t F u n c t i o n . NormSDist ( d2 ) E l s e I f C a l l P u t F l a g = " p " Then B l a c k S c h o l e s V e r 2 = X Exp( r T) _ W o r k s h e e t F u n c t i o n . NormSDist( d2 ) _ S W o r k s h e e t F u n c t i o n . NormSDist( d1 ) End I f End F u n c t i o n
Guy Yollin (Copyright 2012) Data Access with SQL, Excel & VBA Data access with VBA 36 / 45

Outline

Introduction to QueryTables Downloading equity data from Yahoo Finance Read data from SQL databases Implementing VBA worksheet formulas VBA chart creation

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

37 / 45

Section references

John Walkenbach Excel 2010 Power Programming with VBA Sams, 2010 Chapter 18 Duane Birnbaum and Michael Vine Excel VBA Programming for the Absolute Beginner, 3rd Edition Thomson Course Technology, 2007 Chapter 9

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

38 / 45

Record column chart creation process

1 2 3 4

Select A1:A17, H1:H17 Click record macro Insert clustered columns chart Right click and Format Axis
Guy Yollin (Copyright 2012)

6 7 8 9

Change Axis Labels to Low Align text to rotate 270 Delete legend Click stop recording
Data access with VBA 39 / 45

Data Access with SQL, Excel & VBA

Recorded macro
Sub R e c o r d P l o t R e t u r n s ( ) A c t i v e S h e e t . S h a p e s . AddChart . S e l e c t ActiveChart . SetSourceData S o u r c e := Range ( " msft ! $A$1 : $A$17 , msft ! $H$1 : $H$17 " ) A c t i v e C h a r t . ChartType = x l C o l u m n C l u s t e r e d A c t i v e C h a r t . Axes ( x l C a t e g o r y ) . S e l e c t A c t i v e S h e e t . C h a r t O b j e c t s ( " C h a r t 10 " ) . A c t i v a t e S e l e c t i o n . T i c k L a b e l P o s i t i o n = xlLow A c t i v e S h e e t . C h a r t O b j e c t s ( " C h a r t 10 " ) . A c t i v a t e A c t i v e C h a r t . Legend . S e l e c t Selection . Delete End Sub

Hard-coded range Hard-coded chart name (will not be correct the next time) Nothing recorded regarding the text alignment
Guy Yollin (Copyright 2012) Data Access with SQL, Excel & VBA Data access with VBA 40 / 45

First modication to recorded macro


Sub RecordedMod1 ( ) Dim DataRange As Range S e t DataRange = S e l e c t i o n A c t i v e S h e e t . S h a p e s . AddChart . S e l e c t A c t i v e C h a r t . S e t S o u r c e D a t a S o u r c e := DataRange A c t i v e C h a r t . ChartType = x l C o l u m n C l u s t e r e d A c t i v e C h a r t . Axes ( x l C a t e g o r y ) . S e l e c t S e l e c t i o n . T i c k L a b e l P o s i t i o n = xlLow A c t i v e C h a r t . Legend . S e l e c t Selection . Delete End Sub

To-Do: align x-axis labels add a y-axis label change the title change chart position
Guy Yollin (Copyright 2012) Data Access with SQL, Excel & VBA Data access with VBA 41 / 45

Finished Plot Returns procedure


Sub P l o t R e t u r n s ( ) Dim MyChart As C h a r t Dim DataRange As Range S e t MyChart = A c t i v e S h e e t . S h a p e s . AddChart ( x l C o l u m n C l u s t e r e d , 3 7 5 , 5 , 3 5 0 , 2 5 0 ) . C h a r t With MyChart . C h a r t T i t l e . Text = " D a i l y S t o c k R e t u r n s " . Axes ( x l C a t e g o r y ) . T i c k L a b e l s . O r i e n t a t i o n = 90 . Axes ( x l C a t e g o r y ) . T i c k L a b e l P o s i t i o n = x l L o w . HasLegend = F a l s e . Axes ( x l V a l u e , x l P r i m a r y ) . H a s T i t l e = True . Axes ( x l V a l u e , x l P r i m a r y ) . A x i s T i t l e . C h a r a c t e r s . Text = " Log R e t u r n (%) " End With End Sub

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

42 / 45

Alternative plotting method using ChartObjects


Sub P l o t P r i c e s ( ) Dim MyChart As C h a r t Dim DataRange As Range S e t DataRange = S e l e c t i o n S e t MyChart = A c t i v e S h e e t . C h a r t O b j e c t s . Add ( 3 7 5 , 5 , 3 5 0 , 2 5 0 ) . C h a r t With MyChart . S e t S o u r c e D a t a S o u r c e := DataRange . ChartType = x l L i n e M a r k e r s . C h a r t T i t l e . Text = " D a i l y S t o c k P r i c e " . Axes ( x l C a t e g o r y ) . T i c k L a b e l s . O r i e n t a t i o n = 90 . HasLegend = F a l s e . Axes ( x l V a l u e , x l P r i m a r y ) . H a s T i t l e = True . Axes ( x l V a l u e , x l P r i m a r y ) . A x i s T i t l e . C h a r a c t e r s . Text = " P r i c e ( $ ) " End With End Sub

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

43 / 45

Plotting candle stick charts


Sub PlotOHLC ( ) Dim MyChart As C h a r t S e t MyChart = A c t i v e S h e e t . S h a p e s . AddChart ( xlStockOHLC , 3 7 5 , 5 , 3 5 0 , 2 5 0 ) . C h a r t With MyChart . ChartStyle = 4 . H a s T i t l e = True . C h a r t T i t l e . Text = " D a i l y S t o c k P r i c e " . Axes ( x l C a t e g o r y ) . T i c k L a b e l s . O r i e n t a t i o n = 90 . Axes ( x l C a t e g o r y ) . T i c k L a b e l P o s i t i o n = x l L o w . HasLegend = F a l s e . Axes ( x l V a l u e , x l P r i m a r y ) . H a s T i t l e = True . Axes ( x l V a l u e , x l P r i m a r y ) . A x i s T i t l e . C h a r a c t e r s . Text = " P r i c e ( $ ) " End With End Sub

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

44 / 45

Computational Finance and Risk Management

http://depts.washington.edu/compfin

Guy Yollin (Copyright 2012)

Data Access with SQL, Excel & VBA

Data access with VBA

45 / 45