Come si esegue una query di SQL Server da PowerShell?


161

C'è un modo per eseguire una query arbitraria su un SQL Server usando Powershell sul mio computer locale?

Risposte:


166

Per gli altri che hanno bisogno di farlo con solo stock .net e PowerShell (non sono installati ulteriori strumenti SQL) ecco la funzione che uso:

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

}

Lo sto usando da così tanto tempo che non so chi abbia scritto quali parti, ma questo è stato distillato dagli esempi di altri ma semplificato per essere chiaro e solo ciò che è necessario senza dipendenze o funzionalità extra.

Lo uso e lo condivido abbastanza spesso da averlo trasformato in un modulo di script su GitHub in modo da poter ora andare alla directory dei moduli ed eseguirlo git clone https://github.com/ChrisMagnuson/InvokeSQLe da quel momento in poi invoke-sql verrà automaticamente caricato quando si andrà a usarlo (supponendo stai usando PowerShell v3 o successivo).


lo smaltimento non ha importanza qui o in PowerShell?
Maslow,

2
@Maslow Non potrei dirlo con certezza, so che funziona senza lo smaltimento degli oggetti, ma se hai un singolo processo powershell.exe che lo chiamerà più volte per settimane senza chiuderlo, alla fine potrebbe essere un problema ma tu dovrebbe testarlo.
Chris Magnuson,

1
Si noti che questo ti costringe a scrivere script che potrebbero essere vulnerabili agli attacchi di SQL sq injection, se dipendono dalla lettura dei dati per la query da una fonte che si basa sull'input dell'utente.
Joel Coehoorn,

4
@AllTradesJack Google Sql Injection. Il comando Invoke-Sql non ha un modo per includere parametri separati dal testo del comando. Questo praticamente garantisce che hai usato la concatenazione di stringhe per creare le query, e questo è un grande no-no.
Joel Coehoorn,

1
Funziona bene per me. Per chiunque si stia chiedendo, per disporre un oggetto, basta aggiungere $connection.dispose()ecc. Non so se fa alcuna differenza
Nick.McDermaid

101

È possibile utilizzare il Invoke-Sqlcmdcmdlet

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

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


22
Qualcuno dovrebbe dire che questo potrebbe essere ottimo se ti trovi nel contesto del server sql, ma non tanto se stai usando la tua workstation ...
aikeru,

10
È possibile eseguirlo ovunque siano installati gli strumenti client di SQL Server (SSMS). Funziona bene da qualsiasi workstation, sia che esegua SQL Server o meno.
alroc,

3
Utilizzare la seguente importazione per rendere disponibile il cmdlet: Import-Module "sqlps" -DisableNameChecking
xx1xx

1
Se continui a utilizzare SQL 2008 R2, devi utilizzare un modulo di soluzione: sev17.com/2010/07/10/making-a-sqlps-module
Vincent De Smet,

2
Invoke-SqlCmd è un incubo senza fine di casi bizzarri e comportamenti inconsistenti. Perché viene emessa colonne a volte e non altre volte? Dove sono i miei messaggi di errore? Perché è su un computer o non un altro? Come lo installo? La risposta a ciascuna domanda è peggiore della precedente.
Pxtl,

28

Ecco un esempio che ho trovato su questo 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();

Presumibilmente potresti sostituire una diversa istruzione TSQL dove dice dbcc freeproccache.


1
Questa soluzione ha funzionato per me, però, ExecuteNonQuery()è tornato a zero in caso di successo, la condizione che l'uso io è: if ($cmd.ExecuteNonQuery() -ne 0).
Gixabel,

Sembra che restituisca il numero di linee interessate. docs.microsoft.com/en-us/dotnet/api/…
NicolasW

27

Questa funzione restituirà i risultati di una query come una matrice di oggetti PowerShell in modo da poterli utilizzare nei filtri e accedere facilmente alle colonne:

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
}

Perché è preferibile riempire un DataTable(vedi la risposta di Adamo )?
alroc,

2
Probabilmente non c'è una grande differenza, ma SqlDataReaders sono generalmente preferiti perché consumano meno risorse. Non è probabile che sia rilevante qui, ma è bello recuperare oggetti reali invece di un datatable che puoi usare in foreach e dove clausole senza preoccuparti dell'origine dei dati.
mcobrien,

1
un esempio di utilizzo sarebbe carino.
Eric Schneider,

A volte non ci sono abbastanza stelle
Fred B,

13

Se vuoi farlo sul tuo computer locale invece che nel contesto di SQL Server, userò quanto segue. È quello che usiamo nella mia azienda.

$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

Basta compilare le variabili $ ServerName , $ DatabaseName e $ Query e dovresti essere pronto.

Io non sono sicuro di come abbiamo inizialmente trovato questo fuori, ma c'è qualcosa di molto simile qui .


12
Invoke-Sqlcmd -Query "sp_who" -ServerInstance . -QueryTimeout 3

mostrerà il numero di connessione in sql usato dal comando
powershell


0

Per evitare l'iniezione SQL con i parametri varchar è possibile utilizzare


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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.