Como você executa uma consulta do SQL Server no PowerShell?

161

Existe uma maneira de executar uma consulta arbitrária em um SQL Server usando o Powershell na minha máquina local?


fonte

Respostas:

166

Para outras pessoas que precisam fazer isso apenas com o estoque .net e PowerShell (nenhuma ferramenta SQL adicional instalada), aqui está a função que eu 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

}

Eu tenho usado isso há tanto tempo que não sei quem escreveu quais partes, mas isso foi extraído dos exemplos de outras pessoas, mas simplificado para ficar claro e exatamente o que é necessário sem dependências ou recursos extras.

Eu uso e compartilho isso com frequência suficiente para transformá-lo em um módulo de script no GitHub, para que agora você possa ir para o diretório de módulos e executar git clone https://github.com/ChrisMagnuson/InvokeSQLe, a partir desse momento, o invoke-sql seja carregado automaticamente quando você o usar (assumindo você está usando o powershell v3 ou posterior).

Chris Magnuson
fonte
O descarte não importa aqui ou no PowerShell?
Maslow
2
@ Maslow Eu não poderia dizer com certeza, eu sei que isso funciona bem sem descartar os objetos, mas se você tiver um único processo powershell.exe que chamará isso várias vezes durante semanas sem fechar, poderá ser um problema, mas você teria que testar isso.
Chris Magnuson
1
Observe que isso obriga a escrever scripts que podem ser vulneráveis ​​a ataques de injeção de sql, se eles dependem da leitura de dados para a consulta de uma fonte que depende da entrada do usuário.
Joel Coehoorn
4
@AllTradesJack Google Sql Injection. O comando Invoke-Sql não tem como incluir parâmetros separados do texto do comando. Isso praticamente garante que você usou a concatenação de strings para criar as consultas, e isso é um grande não-não.
Joel Coehoorn
1
Funciona bem para mim. Para quem se perguntando, se desfazer de um objeto, basta adicionar $connection.dispose()etc. Eu não sei se isso faz alguma diferença, embora
Nick.McDermaid
101

Você pode usar o Invoke-Sqlcmdcmdlet

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

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

manojlds
fonte
22
Alguém deveria mencionar isso pode ser ótimo se você está no contexto do servidor SQL, mas não tanto se você estiver usando sua estação de trabalho ...
aikeru
10
Você pode executá-lo em qualquer lugar em que as ferramentas de cliente do SQL Server (SSMS) estejam instaladas. Funciona bem em qualquer estação de trabalho, esteja executando o SQL Server ou não.
Alroc 21/09
3
Use a seguinte importação para disponibilizar o cmdlet: Import-Module "sqlps" -DisableNameChecking
xx1xx
1
Se você ainda está em SQL 2008 R2 você precisa usar um trabalho em torno do módulo: sev17.com/2010/07/10/making-a-sqlps-module
Vincent De Smet
2
Invoke-SqlCmd é um pesadelo interminável de casos extremos bizarros e comportamento inconsistente. Por que está produzindo colunas algumas vezes e outras não? Onde estão minhas mensagens de erro? Por que ele está em um computador ou não em outro? Como instalo? A resposta para cada pergunta é pior que a anterior.
Pxtl
28

Aqui está um exemplo que encontrei neste 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();

Presumivelmente, você pode substituir uma instrução TSQL diferente, onde ela diz dbcc freeproccache.

dmc
fonte
1
Esta solução funcionou para mim, no entanto, ExecuteNonQuery()voltou de zero em caso de sucesso, a condição de que eu uso é: if ($cmd.ExecuteNonQuery() -ne 0).
Gixabel
Parece que retorna o número de linhas afetadas. docs.microsoft.com/en-us/dotnet/api/…
NicolasW 02/02/19
27

Esta função retornará os resultados de uma consulta como uma matriz de objetos do PowerShell, para que você possa usá-los em filtros e acessar colunas facilmente:

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
fonte
Por que isso é preferível a preencher um DataTable(veja a resposta de Adam )?
Alroc
2
Provavelmente não há uma grande diferença, mas SqlDataReaders geralmente são preferidos porque consomem menos recursos. Provavelmente não é relevante aqui, mas é bom recuperar objetos reais em vez de uma tabela de dados que você pode usar nas cláusulas foreach e where sem se preocupar com a fonte dos dados.
Mcobrien
1
um exemplo de uso seria bom.
Eric Schneider
Às vezes não há o suficiente estrelas
Fred B
13

Se você quiser fazê-lo em sua máquina local, e não no contexto do servidor SQL, eu usaria o seguinte. É o que usamos na minha empresa.

$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 preencher as variáveis $ ServerName , $ DatabaseName e $ Query e você deve estar pronto.

Não tenho certeza de como descobrimos isso originalmente, mas há algo muito semelhante aqui .

Adão
fonte
12
Invoke-Sqlcmd -Query "sp_who" -ServerInstance . -QueryTimeout 3
arnav
fonte
ele vai mostrar número de conexão no SQL usada pelo comando PowerShell
arnav
0

Para evitar a injeção de SQL com parâmetros varchar, você pode usar


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
fonte