Jak uruchomić zapytanie SQL Server z PowerShell?

161

Czy istnieje sposób wykonania dowolnego zapytania na serwerze SQL przy użyciu programu PowerShell na moim komputerze lokalnym?


źródło

Odpowiedzi:

166

Dla innych, którzy muszą to zrobić za pomocą zwykłego .net i PowerShell (bez zainstalowanych dodatkowych narzędzi SQL), oto funkcja, której używam:

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

}

Używam tego od tak dawna, że ​​nie wiem, kto napisał, które części, ale zostało to wydestylowane z przykładów innych, ale uproszczone, aby było jasne i tylko to, co jest potrzebne, bez dodatkowych zależności lub funkcji.

Używam i udostępniam to na tyle często, że zamieniłem to w moduł skryptu na GitHub , abyś mógł teraz przejść do katalogu modułów i wykonać, git clone https://github.com/ChrisMagnuson/InvokeSQLa od tego momentu invoke-sql zostanie automatycznie załadowany, gdy zaczniesz go używać (zakładając korzystasz z PowerShell w wersji 3 lub nowszej).

Chris Magnuson
źródło
czy utylizacja nie ma znaczenia tutaj czy w PowerShell?
Maslow
2
@Maslow Nie mogę powiedzieć na pewno, wiem, że to działa dobrze bez usuwania obiektów, ale jeśli masz pojedynczy proces powershell.exe, który będzie wywoływał to wiele razy w ciągu tygodni bez zamykania, może to w końcu być problem, ale ty musiałby to sprawdzić.
Chris Magnuson
1
Zwróć uwagę, że zmusza to do pisania skryptów, które mogą być podatne na ataki typu sql injection, jeśli polegają na odczytywaniu danych zapytania ze źródła, które opiera się na danych wejściowych użytkownika.
Joel Coehoorn
4
@AllTradesJack Google Sql Injection. Polecenie Invoke-Sql nie umożliwia dołączania parametrów oddzielnie od tekstu polecenia. To prawie gwarantuje, że użyłeś konkatenacji ciągów do zbudowania zapytań, a to duże nie-nie.
Joel Coehoorn
1
U mnie działa dobrze. Dla każdego, kto się zastanawia, aby pozbyć się przedmiotu, po prostu dodaj $connection.dispose()itp. Nie wiem, czy to robi jakąkolwiek różnicę
Nick.McDermaid
101

Możesz użyć polecenia Invoke-Sqlcmdcmdlet

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

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

manojlds
źródło
22
Ktoś powinien wspomnieć, że może to być świetne, jeśli jesteś w kontekście serwera sql, ale nie tak bardzo, jeśli używasz stacji roboczej ...
aikeru
10
Można to uruchomić wszędzie tam, gdzie są zainstalowane narzędzia klienta SQL Server (SSMS). Działa dobrze na każdej stacji roboczej, niezależnie od tego, czy jest na niej uruchomiony SQL Server, czy nie.
alroc
3
Użyj następującego importu, aby mieć dostępne polecenie cmdlet: Import-Module "sqlps" -DisableNameChecking
xx1xx
1
Jeśli nadal korzystasz z SQL 2008 R2, musisz skorzystać z modułu obejścia: sev17.com/2010/07/10/making-a-sqlps-module
Vincent De Smet,
2
Invoke-SqlCmd to niekończący się koszmar dziwacznych przypadków skrajnych i niespójnego zachowania. Dlaczego czasami wyświetla kolumny, a innym razem nie? Gdzie są moje komunikaty o błędach? Dlaczego jest na jednym komputerze, czy nie na innym? Jak to zainstalować? Odpowiedź na każde pytanie jest gorsza niż poprzednie.
Pxtl
28

Oto przykład, który znalazłem na tym blogu .

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

Przypuszczalnie można by zastąpić inną instrukcję TSQL tam, gdzie jest napisana dbcc freeproccache.

dmc
źródło
1
To rozwiązanie pracował dla mnie, jednak ExecuteNonQuery()wrócił do zera w przypadku sukcesu, warunek, że używam to: if ($cmd.ExecuteNonQuery() -ne 0).
Gixabel
Wygląda na to, że zwraca liczbę dotkniętych linii. docs.microsoft.com/en-us/dotnet/api/…
NicolasW
27

Ta funkcja zwróci wyniki zapytania jako tablicę obiektów PowerShell, dzięki czemu można ich używać w filtrach i łatwo uzyskiwać dostęp do kolumn:

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
źródło
Dlaczego jest to lepsze od wypełniania DataTable(patrz odpowiedź Adama )?
alroc
2
Prawdopodobnie nie ma dużej różnicy, ale SqlDataReaders są ogólnie preferowane, ponieważ zużywają mniej zasobów. To raczej nie ma znaczenia w tym przypadku, ale miło jest odzyskać prawdziwe obiekty zamiast danych, których można używać w klauzulach foreach i where bez martwienia się o źródło danych.
mcobrien
1
przykładowe użycie byłoby miłe.
Eric Schneider
Czasami jest za mało gwiazd
Fred B
13

Jeśli chcesz to zrobić na komputerze lokalnym, a nie w kontekście serwera SQL, użyłbym następującego. To jest to, czego używamy w mojej firmie.

$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

Wystarczy wypełnić zmienne $ ServerName , $ DatabaseName i $ Query i wszystko powinno być gotowe.

Nie jestem pewien, jak pierwotnie znaleźć na to uwagę, ale jest coś bardzo podobnego tutaj .

Adam
źródło
12
Invoke-Sqlcmd -Query "sp_who" -ServerInstance . -QueryTimeout 3
arnav
źródło
pokaże liczbę połączeń w sql używanych przez polecenie powershell
arnav
0

Aby uniknąć iniekcji SQL z parametrami varchar, możesz użyć


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
źródło