Is there a way to execute an arbitrary query on a SQL Server using Powershell on my local machine?
10 Answers
For others who need to do this with just stock .NET and PowerShell (no additional SQL tools installed) here is the function that I use:
function Invoke-SQL {
param(
[string] $dataSource = ".\SQLEXPRESS",
[string] $database = "MasterData",
[string] $sqlCommand = $(throw "Please specify a query.")
)
$connectionString = "Data Source=$dataSource; " +
"Integrated Security=SSPI; " +
"Initial Catalog=$database"
$connection = new-object system.data.SqlClient.SQLConnection($connectionString)
$command = new-object system.data.sqlclient.sqlcommand($sqlCommand,$connection)
$connection.Open()
$adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command
$dataset = New-Object System.Data.DataSet
$adapter.Fill($dataSet) | Out-Null
$connection.Close()
$dataSet.Tables
}
I have been using this so long I don't know who wrote which parts. This was distilled from others' examples, but simplified to be clear and just what is needed without extra dependencies or features.
I use and share this often enough that I have turned this into a script module on GitHub so that you can now go to your modules directory and execute git clone https://github.com/ChrisMagnuson/InvokeSQL
and from that point forward invoke-sql will automatically be loaded when you go to use it (assuming your using PowerShell v3 or later).

- 20,799
- 66
- 75
- 101

- 5,780
- 7
- 34
- 37
-
does disposal not matter here or in powershell? – Maslow May 27 '14 at 15:44
-
2@Maslow I couldn't say for sure, I know that this works fine without disposing of the objects but if you have a single powershell.exe process that will call this multiple times over weeks without closing then it might eventually be an issue but you would have to test that. – Chris Magnuson May 28 '14 at 14:36
-
1Note that this forces you to write scripts that may be vulnerable to sql injection attacks, if they depend on reading data for the query from a source that relies on user input. – Joel Coehoorn Jun 17 '14 at 21:38
-
1@JoelCoehoorn could you explain a bit more for uninitiated among us? – AllTradesJack Jan 13 '17 at 20:00
-
5@AllTradesJack Google Sql Injection. The Invoke-Sql command doesn't have a way to include parameters separate from the command text. This pretty much guarantees you used string concatenation to build the queries, and that's a big no-no. – Joel Coehoorn Jan 14 '17 at 01:36
-
I tried this and got an error on the adapter.fill operation. I was trying to add a "WHERE" to the command. [string] $sqlCommand = 'SELECT "First Name", "Effective Status" FROM Person.Snapshot WHERE "Effective Status" = "current"' IT keeps telling me the colum "current" is invalid? – DevilWAH Jan 20 '17 at 14:45
-
2Works well for me. For anyone wondering, to dispose an object, just add `$connection.dispose()` etc. I don't know if it makes any difference though – Nick.Mc Aug 15 '17 at 11:34
-
1I added here a version to avoid SQL Injection https://stackoverflow.com/a/62071742 let me know what do you think about it. Is useful when the query is composed and the parameters are from a generic, not-trusted, source. – Piero May 29 '20 at 08:03
-
For anyone else getting Exception calling "Fill" with "1" argument(s): "Invalid object name 'TableName'." You need to fully qualify the table name in your code with DatabaseName.TableName – David Klempfner Nov 30 '20 at 22:15
-
you should include parameters to be passed: foreach($p in $parameters.Keys){ [Void] $cmd.Parameters.AddWithValue("@$p",$parameters[$p]) } – Golden Lion Dec 16 '20 at 22:00
-
$params=@{field1='hello';field2='world';} – Golden Lion Dec 16 '20 at 23:00
You can use the Invoke-Sqlcmd
cmdlet
Invoke-Sqlcmd -Query "SELECT GETDATE() AS TimeOfQuery;" -ServerInstance "MyComputer\MyInstance"

- 290,304
- 63
- 469
- 417
-
31Someone should mention this may be great if you are in the context of the sql server, but not so much if you are using your workstation ... – aikeru Jun 27 '13 at 22:07
-
14You can run this anywhere the SQL Server client tools (SSMS) are installed. It works fine from any workstation, whether it's running SQL Server or not. – alroc Sep 21 '13 at 17:56
-
3Use the following import to have the cmdlet available: `Import-Module "sqlps" -DisableNameChecking` – xx1xx Mar 21 '14 at 05:52
-
1If you're still on SQL 2008 R2 you need to use a work around module: http://sev17.com/2010/07/10/making-a-sqlps-module/ – Vincent De Smet Dec 21 '15 at 09:10
-
4Invoke-SqlCmd is an endless nightmare of bizarre edge-cases and inconsistent behavior. Why is it outputting columns sometimes and not other times? Where are my error messages? Why is it on one computer or not another? How do I install it? The answer to each question is worse than the last. – Pxtl Aug 07 '18 at 21:29
-
1`Invoke-Sqlcmd` is fine if you're writing the script to automate some query you would otherwise build and issue yourself, but as it doesn't support parameters it's an extremely bad idea for anything that accepts input from an untrusted source. You would think the `-Variables` option would let you prevent SQL injection, but it doesn't. See http://www.cjsommer.com/2015-10-13-running-parameterized-queries-against-sql-server-using-powershell/. – Soren Bjornstad Jan 18 '19 at 12:39
-
I also could not bring `Invoke-Sqlcmd` to work on a remote SQL-Server, while the above solution (although quite old) worked for me. – Michael W. Mar 22 '21 at 14:17
This function will return the results of a query as an array of powershell objects so you can use them in filters and access columns easily:
function sql($sqlText, $database = "master", $server = ".")
{
$connection = new-object System.Data.SqlClient.SQLConnection("Data Source=$server;Integrated Security=SSPI;Initial Catalog=$database");
$cmd = new-object System.Data.SqlClient.SqlCommand($sqlText, $connection);
$connection.Open();
$reader = $cmd.ExecuteReader()
$results = @()
while ($reader.Read())
{
$row = @{}
for ($i = 0; $i -lt $reader.FieldCount; $i++)
{
$row[$reader.GetName($i)] = $reader.GetValue($i)
}
$results += new-object psobject -property $row
}
$connection.Close();
$results
}

- 1,130
- 1
- 9
- 16
-
Why is this preferable over filling a `DataTable` (see [Adam's answer](http://stackoverflow.com/a/17497834/1324345))? – alroc Aug 01 '13 at 11:20
-
3There probably isn't a huge difference, but SqlDataReaders are generally preferred because they consume less resources. That isn't likely to be relevant here but it is nice to get real objects back instead of a datatable that you can use in foreach and where clauses without worrying about the source of the data. – mcobrien Aug 01 '13 at 12:08
-
1
-
Here's an example I found on this blog.
$cn2 = new-object system.data.SqlClient.SQLConnection("Data Source=machine1;Integrated Security=SSPI;Initial Catalog=master");
$cmd = new-object system.data.sqlclient.sqlcommand("dbcc freeproccache", $cn2);
$cn2.Open();
if ($cmd.ExecuteNonQuery() -ne -1)
{
echo "Failed";
}
$cn2.Close();
Presumably you could substitute a different TSQL statement where it says dbcc freeproccache
.

- 2,596
- 21
- 24
-
1This solution worked for me, however, `ExecuteNonQuery()` returned zero on success, the condition that I use is: `if ($cmd.ExecuteNonQuery() -ne 0)`. – Gixabel Apr 10 '18 at 21:53
-
1Seems it returns the number of lines impacted. https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand.executenonquery?view=netframework-4.7.2 – NicolasW Feb 02 '19 at 00:55
-
This approach consistently failed without a useful error message so I use this approach, even though it requires installation of additional modules https://learn.microsoft.com/en-us/powershell/module/sqlserver/invoke-sqlcmd?view=sqlserver-ps#example-11-connect-to-azure-sql-database-(or-managed-instance)-using-an-access-token – Nick.Mc Feb 02 '23 at 11:09
If you want to do it on your local machine instead of in the context of SQL server then I would use the following. It is what we use at my company.
$ServerName = "_ServerName_"
$DatabaseName = "_DatabaseName_"
$Query = "SELECT * FROM Table WHERE Column = ''"
#Timeout parameters
$QueryTimeout = 120
$ConnectionTimeout = 30
#Action of connecting to the Database and executing the query and returning results if there were any.
$conn=New-Object System.Data.SqlClient.SQLConnection
$ConnectionString = "Server={0};Database={1};Integrated Security=True;Connect Timeout={2}" -f $ServerName,$DatabaseName,$ConnectionTimeout
$conn.ConnectionString=$ConnectionString
$conn.Open()
$cmd=New-Object system.Data.SqlClient.SqlCommand($Query,$conn)
$cmd.CommandTimeout=$QueryTimeout
$ds=New-Object system.Data.DataSet
$da=New-Object system.Data.SqlClient.SqlDataAdapter($cmd)
[void]$da.fill($ds)
$conn.Close()
$ds.Tables
Just fill in the $ServerName, $DatabaseName and the $Query variables and you should be good to go.
I am not sure how we originally found this out, but there is something very similar here.

- 571
- 1
- 6
- 8
There isn't a built-in "PowerShell" way of running a SQL query. If you have the SQL Server tools installed, you'll get an Invoke-SqlCmd cmdlet.
Because PowerShell is built on .NET, you can use the ADO.NET API to run your queries.

- 25,861
- 15
- 82
- 91
Invoke-Sqlcmd -Query "sp_who" -ServerInstance . -QueryTimeout 3

- 50,140
- 28
- 121
- 140

- 3,383
- 1
- 24
- 23
To avoid SQL Injection with varchar parameters you could use
function sqlExecuteRead($connectionString, $sqlCommand, $pars) {
$connection = new-object system.data.SqlClient.SQLConnection($connectionString)
$connection.Open()
$command = new-object system.data.sqlclient.sqlcommand($sqlCommand, $connection)
if ($pars -and $pars.Keys) {
foreach($key in $pars.keys) {
# avoid injection in varchar parameters
$par = $command.Parameters.Add("@$key", [system.data.SqlDbType]::VarChar, 512);
$par.Value = $pars[$key];
}
}
$adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command
$dataset = New-Object System.Data.DataSet
$adapter.Fill($dataset) | Out-Null
$connection.Close()
return $dataset.tables[0].rows
}
$connectionString = "connectionstringHere"
$sql = "select top 10 Message, TimeStamp, Level from dbo.log " +
"where Message = @MSG and Level like @LEVEL"
$pars = @{
MSG = 'this is a test from powershell'
LEVEL = 'aaa%'
};
sqlExecuteRead $connectionString $sql $pars

- 937
- 16
- 29

- 354
- 1
- 12
-
I can't image a scenario where a PS script would be susceptible to SQL Injection, but I prefer this to manually building the query. I think it is more readable. Thanks! – 2b77bee6-5445-4c77-b1eb-4df3e5 Dec 09 '20 at 21:38
You can even format string and pass parameters as you want.
case "ADDSQLSERVERUSER":
//0 = coprorateName;
//1 = user password
//2 = servername
command = @"$sqlQuery = Use JazzUWS_'{0}'
Create login UWSUser_'{0}' with password='{1}';
Create user UWSUser_'{0}' for login UWSUser_'{0}';
Grant Execute to UWSUser_'{0}';
Use ReportSvrUWS_'{0}'
Create user UWSUser_'{0}' for login UWSUser_'{0}';
Grant Execute to UWSUser_'{0}';
Invoke-Sqlcmd -Query $sqlQuery -ServerInstance '{2}'";
break;
C# Code for remote execution(you can organize your way)
string script = PowershellDictionary.GetPowershellCommand("ADDSQLSERVERUSER");
script = String.Format(script, this.CorporateName, password, this.SQLServerName)
PowerShellExecution.RunScriptRemote(_credentials.Server, _credentials.Username, _credentials.Password, new List<string> { script });

- 3,074
- 1
- 30
- 43
You could use the best SQL Server module around: DBATOOLS. You would also benefit from running a query to multiple sql instances.
Install-Module dbatools -Scope CurrentUser
$sql = 'SQL1','SQL1\INSTANCE1','SQL2'
$query = "SELECT 'This query would run on all SQL instances'"
Invoke-DbaQuery -SqlInstance $sqlinstances -Query $query -AppendServerInstance

- 1,726
- 2
- 22
- 31