Wie führen Sie eine SQL Server-Abfrage in PowerShell aus?

161

Gibt es eine Möglichkeit, eine beliebige Abfrage auf einem SQL Server mit Powershell auf meinem lokalen Computer auszuführen?


quelle

Antworten:

166

Für andere, die dies nur mit Standard-.net und PowerShell tun müssen (keine zusätzlichen SQL-Tools installiert), ist hier die Funktion, die ich verwende:

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

}

Ich benutze dies so lange, dass ich nicht weiß, wer welche Teile geschrieben hat, aber dies wurde aus den Beispielen anderer herausgearbeitet, aber vereinfacht, um klar zu sein und genau das, was ohne zusätzliche Abhängigkeiten oder Funktionen benötigt wird.

Ich benutze und teile dies oft genug, so dass ich es in ein Skriptmodul auf GitHub verwandelt habe, so dass Sie jetzt in Ihr Modulverzeichnis gehen und es ausführen können. git clone https://github.com/ChrisMagnuson/InvokeSQLVon diesem Punkt an wird invoke-sql automatisch geladen, wenn Sie es verwenden (vorausgesetzt Sie verwenden Powershell v3 oder höher.

Chris Magnuson
quelle
spielt die Entsorgung hier oder in Powershell keine Rolle?
Maslow
2
@Maslow Ich kann nicht sicher sagen, ich weiß, dass dies gut funktioniert, ohne die Objekte zu entsorgen, aber wenn Sie einen einzelnen Powershell.exe-Prozess haben, der dies über Wochen mehrmals aufruft, ohne zu schließen, dann könnte es irgendwann ein Problem sein, aber Sie müsste das testen.
Chris Magnuson
1
Beachten Sie, dass Sie dadurch gezwungen werden, Skripts zu schreiben, die möglicherweise für SQL-Injection-Angriffe anfällig sind, wenn sie vom Lesen von Daten für die Abfrage aus einer Quelle abhängen, die auf Benutzereingaben beruht.
Joel Coehoorn
4
@AllTradesJack Google SQL Injection. Der Befehl Invoke-Sql kann keine vom Befehlstext getrennten Parameter einschließen. Dies garantiert so ziemlich, dass Sie zum Erstellen der Abfragen die Verkettung von Zeichenfolgen verwendet haben, und das ist ein großes Nein-Nein.
Joel Coehoorn
1
Funktioniert gut für mich. Für alle, die sich fragen, ein Objekt zu entsorgen, einfach hinzuzufügen $connection.dispose()usw. Ich weiß nicht, ob es einen Unterschied macht
Nick.McDermaid
101

Sie können das Invoke-SqlcmdCmdlet verwenden

Invoke-Sqlcmd -Query "SELECT GETDATE() AS TimeOfQuery;" -ServerInstance "MyComputer\MyInstance"

http://technet.microsoft.com/en-us/library/cc281720.aspx

Manojlds
quelle
22
Jemand sollte erwähnen, dass dies großartig sein kann, wenn Sie sich im Kontext des SQL-Servers befinden, aber nicht so sehr, wenn Sie Ihre Workstation verwenden ...
aikeru
10
Sie können dies überall dort ausführen, wo die SQL Server-Clienttools (SSMS) installiert sind. Es funktioniert von jeder Workstation aus, unabhängig davon, ob SQL Server ausgeführt wird oder nicht.
Alroc
3
Verwenden Sie den folgenden Import, um das Cmdlet verfügbar zu machen: Import-Module "sqlps" -DisableNameChecking
xx1xx
1
Wenn Sie noch mit SQL 2008 R2 arbeiten, müssen Sie ein Workaround-
Vincent De Smet
2
Invoke-SqlCmd ist ein endloser Albtraum bizarrer Randfälle und inkonsistenten Verhaltens. Warum werden manchmal und nicht zu anderen Zeiten Spalten ausgegeben? Wo sind meine Fehlermeldungen? Warum ist es auf einem Computer oder nicht auf einem anderen? Wie installiere ich es? Die Antwort auf jede Frage ist schlechter als die letzte.
Pxtl
28

Hier ist ein Beispiel, das ich in diesem Blog gefunden habe .

$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();

Vermutlich könnten Sie eine andere TSQL-Anweisung ersetzen, in der es heißt dbcc freeproccache.

dmc
quelle
1
Diese Lösung hat bei mir funktioniert, hat jedoch ExecuteNonQuery()bei Erfolg null zurückgegeben. Die Bedingung, die ich verwende, ist : if ($cmd.ExecuteNonQuery() -ne 0).
Gixabel
Scheint, als würde die Anzahl der betroffenen Zeilen zurückgegeben. docs.microsoft.com/en-us/dotnet/api/…
NicolasW
27

Diese Funktion gibt die Ergebnisse einer Abfrage als Array von Powershell-Objekten zurück, sodass Sie sie problemlos in Filtern verwenden und auf Spalten zugreifen können:

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
}
mcobrien
quelle
Warum ist dies dem Ausfüllen von a vorzuziehen DataTable(siehe Adams Antwort )?
Alroc
2
Es gibt wahrscheinlich keinen großen Unterschied, aber SqlDataReaders werden im Allgemeinen bevorzugt, weil sie weniger Ressourcen verbrauchen. Das ist hier wahrscheinlich nicht relevant, aber es ist schön, echte Objekte anstelle einer Datentabelle zurückzubekommen, die Sie in foreach- und where-Klauseln verwenden können, ohne sich um die Datenquelle zu kümmern.
McBrien
1
Eine beispielhafte Verwendung wäre schön.
Eric Schneider
Manchmal gibt es nicht genug Sterne
Fred B
13

Wenn Sie dies auf Ihrem lokalen Computer anstatt im Kontext von SQL Server tun möchten, würde ich Folgendes verwenden. Es ist das, was wir in meiner Firma verwenden.

$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

Geben Sie einfach $ ServerName , $ DatabaseName und $ Query ein Variablen und Sie sollten gut zu gehen.

Ich bin nicht sicher , wie wir ursprünglich diesen herausgefunden, aber es ist etwas sehr ähnliche hier .

Adam
quelle
12
Invoke-Sqlcmd -Query "sp_who" -ServerInstance . -QueryTimeout 3
Arnav
quelle
Es wird die Anzahl der Verbindungen in SQL anzeigen, die vom Powershell-Befehl verwendet werden
Arnav
0

Um SQL Injection mit varchar-Parametern zu vermeiden, können Sie verwenden


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 = "..."
    $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
Piero
quelle