Public/Invoke-OleDbQuery.ps1
function Invoke-OleDbQuery { <# .SYNOPSIS Queries an OleDb data source, given an OleDb provider, DataSource and query. Returns a DataTable .DESCRIPTION Queries an OleDb data source, given an OleDb provider, DataSource and query. Returns a DataTable www.connectionstrings.com is an excellent resource for connection strings for specfic drivers. .OUTPUTS DataSet, DataTable, DataRow, SingleValue or None. This is controlled by the -As parameter. .PARAMETER Connection If the caller provides a "live", open connection, it will be used. The connection will not be closed. .PARAMETER ConnectionString If the caller provides a connection string, it will be used. The connection will be closed before returning. .PARAMETER DataSource This highly dependant on the provider to be used. For MDB, DBF, EXCEL, etc, it's a file path. For RDBMS like SQL Server or MySQL, it will be a server hostname. Other variations are possible. .PARAMETER Provider Which OleDb provider should be used? .PARAMETER ExtendedProperties Some providers use a bevy of 'extended properties' in the connection string, this is a bucket into which you can throw them. The exact capability and syntax is provider-specific. The are usually key-value pairs in the format "Prop=something;AnotherProp=SomethingElse". No attempt is made to validate these properties or even validate the string. The first indication of trouble is usually that the Open() call will fail. .PARAMETER CommandText A/K/A "Query". What query should be run against the source? At it's simplest, this allows you to specify the table of interest and a specific list of columns. You can use "select *" if you want to. You can also join in the source, provide WHERE clauses, etc. For parameterized queries: 1. Use ? as a placeholder and NOT @SomeName (which is SqlClient-specific and doesn't work with OleDb.) 2. Do not need to specify quotes when providing a parameterizing: "select * from dept where deptname = ?", not "select * from dept where deptname = '?'" .PARAMETER CommandTimeout This limits the running time on the query for the source data. .PARAMETER As This determines the type of object returned to the caller or passed down the pipeline. Currently, the following object types are valid: "DataSet", "DataTable", "DataRow","SingleValue" or "NonQuery". Detail is "DataRow" .PARAMETER SqlParameters Specifies a hashtable of parameters for parameterized SQL queries. http://blog.codinghorror.com/give-me-parameterized-sql-or-give-me-death/ The values should be in a hash, which is easily/invisibly cast to an IDictionary by PowerShell). See the example. .PARAMETER Credential Use alternative credentials. Accepts credential objects provided by Get-Credential. .EXAMPLE Invoke-OleDbQuery -provider:"vfpoledb" -DataSource:'c:\temp\ADOLoad' -query:"Select count(*) CountOf from UNIT" This reads a Visual Fox Pro table, which is similar to dBase, xBase, etc, using the Visual FoxPro OleDb driver. .EXAMPLE Invoke-OleDbQuery -provider:"Microsoft.ACE.OLEDB.12.0" -DataSource:'c:\temp\ADOLoad' -query:"Select count(*) CountOf from UNIT" -ExtendedProperties:"dBASE IV;User ID=Admin;" This reads a Visual Fox Pro table, which is similar to dBase, xBase, etc, using "ACE", which are the drivers that come with Office 2007 and later. Watch out for the version numbers, they change with each version of Office. .EXAMPLE Invoke-OleDbQuery -DataSource 'hal9000' -Provider 'sqloledb' -ExtendedProperties "Trusted_Connection=Yes" -query 'select getdate() RightNow' This reads a SQL Server database. .EXAMPLE $Query = "select getdate() RightNow where getdate() > ? " $Params = @{WasThen = "1/1/1980"} $Report = Invoke-OLEDBQuery -DataSource $DataSource -Provider 'sqloledb' -ExtendedProperties "Trusted_Connection=Yes" -query $Query -SqlParameters $params This shows a parameterized query. This query uses the ? as a placeholder and not @SomeName becuase we are using oledb and not sqlclient. .LINK http://stackoverflow.com/questions/10149910/use-powershell-to-save-a-datatable-as-a-csv .LINK Invoke-DbaQuery I have lifted some ideas straight out of CodingHorror/SqlCollaborative's work. .LINK https://www.connectionstrings.com/ #> param ( [Parameter( ParameterSetName = 'WithConnection', Mandatory = $true )] [System.Data.OleDb.OleDbConnection] $Connection, [Parameter( ParameterSetName = 'WithDataSource', Mandatory = $true )] [string] $DataSource, [Parameter( ParameterSetName = 'WithDataSource', Mandatory = $true )] [string] $Provider, [Parameter( ParameterSetName = 'WithDataSource' )] [string] $ExtendedProperties, [Parameter( ParameterSetName = 'WithConnectionString', Mandatory = $true )] [string] $ConnectionString, [Parameter(Mandatory = $true)] [Alias('Query')] [string] $CommandText, [int] $CommandTimeout = 300, [ValidateSet('DataSet', 'DataTable', 'DataRow', 'SingleValue', 'NonQuery')] [string] $As = 'DataRow', [System.Collections.IDictionary] $SqlParameters, [Parameter( ParameterSetName = 'WithDataSource' )] [System.Management.Automation.PSCredential] $Credential ) <# This is a mashup of that SO.com link and Invoke-SQLCmd2 (which was an ancestor of DbaTools\Invoke-DbaQuery), mainly to get the -AS functionality the root nugget is that we change a datatable to a dataset, then we can pick out the da table if the caller wants it. The code is pretty much boilerplate. #> Try { # If we were handed a connection, use it. If we were not, create one. switch ($PSCmdlet.ParameterSetName) { 'WithConnection' { $OleDbConn = $Connection } 'WithConnectionString' { $OleDbConn = Get-OleDbConnection -ConnectionString $ConnectionString } 'WithDataSource' { $OleDbConn = Get-OleDbConnection -DataSource $DataSource -ExtendedProperties $ExtendedProperties -Provider $Provider -Credential $Credential } } $command = New-Object System.Data.OleDb.OleDbCommand $command.Connection = $OleDbConn $command.CommandTimeout = $CommandTimeout $command.CommandText = $CommandText if ($null -ne $SqlParameters) { $SqlParameters.GetEnumerator() | ForEach-Object { if ($null -ne $_.Value) { $command.Parameters.AddWithValue($_.Key, $_.Value) } else { $command.Parameters.AddWithValue($_.Key, [DBNull]::Value) } } | Out-Null } <# IF we are running a 'non-query', which is a SQL statement that does not return a result set, we need to call .ExecuteNonQuery(). Otherwise, all statements that return a result set need to create a Dataset object and then fill #> switch ($As) { 'NonQuery' { # "$Null = " is alledged to be slighlty faster than " | Out-Null", in most cases $Null = $command.ExecuteNonQuery() } default { $ds = New-Object System.Data.DataSet $da = New-Object system.Data.OleDb.OleDbDataAdapter($command) # "$Null = " is alledged to be slighlty faster than " | Out-Null", in most cases $Null = $da.Fill($ds) } } <# Now, we just need to figure out what object type to return to the caller/send down the pipeline #> switch ($As) { 'NonQuery' { <# This particular query does not return a result set (ex: an UPDATE statement or a DELETE statement), so there is nothing to return to the caller or feed to the pipeline. #> } 'DataSet' { $ds } 'DataTable' { $ds.Tables } 'DataRow' { $ds.Tables[0] } 'SingleValue' { $ds.Tables[0] | Select-Object -ExpandProperty $ds.Tables[0].Columns[0].ColumnName } 'PSObject' { Throw "This function does not implement 'PSObject'." } } } Catch { Throw } Finally { # if we were passed a connection, do not close it. Closing it is the responsibility of the caller. if ($PSCmdlet.ParameterSetName -ne 'WithConnection') { if ($OleDbConn) { $OleDbConn.Close() $OleDbConn.Dispose() } } } } |