Bagaimana Anda menjalankan kueri SQL Server dari PowerShell?


161

Apakah ada cara untuk mengeksekusi permintaan arbitrer pada SQL Server menggunakan Powershell di mesin lokal saya?

Jawaban:


166

Bagi yang lain yang perlu melakukan ini hanya dengan stok .net dan PowerShell (tidak ada alat SQL tambahan yang diinstal) di sini adalah fungsi yang saya gunakan:

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

}

Saya telah menggunakan ini begitu lama saya tidak tahu siapa yang menulis bagian mana tetapi ini disaring dari contoh lain tetapi disederhanakan menjadi jelas dan hanya apa yang dibutuhkan tanpa ketergantungan atau fitur tambahan.

Saya menggunakan dan membagikan ini cukup sering sehingga saya telah mengubah ini menjadi modul skrip di GitHub sehingga Anda sekarang dapat pergi ke direktori modul Anda dan menjalankan git clone https://github.com/ChrisMagnuson/InvokeSQLdan mulai saat itu invoke-sql akan secara otomatis dimuat ketika Anda menggunakannya (dengan asumsi Anda menggunakan PowerShell v3 atau yang lebih baru).


Apakah pembuangan tidak penting di sini atau di PowerShell?
Maslow

2
@Maslow Saya tidak bisa mengatakan dengan pasti, saya tahu bahwa ini berfungsi dengan baik tanpa membuang benda-benda tetapi jika Anda memiliki proses powershell.exe tunggal yang akan memanggil ini beberapa kali selama beberapa minggu tanpa menutup maka pada akhirnya mungkin akan menjadi masalah tetapi Anda harus menguji itu.
Chris Magnuson

1
Perhatikan bahwa ini memaksa Anda untuk menulis skrip yang mungkin rentan terhadap serangan injeksi sql, jika mereka bergantung pada membaca data untuk kueri dari sumber yang bergantung pada input pengguna.
Joel Coehoorn

4
@AllTradesJack Google Sql Injection. Perintah Invoke-Sql tidak memiliki cara untuk memasukkan parameter yang terpisah dari teks perintah. Ini cukup banyak menjamin Anda menggunakan penggabungan string untuk membangun kueri, dan itu adalah no-no besar.
Joel Coehoorn

1
Bekerja dengan baik untuk saya. Bagi siapa pun yang bertanya-tanya, untuk membuang objek, cukup tambahkan $connection.dispose()dll. Saya tidak tahu apakah itu ada bedanya
Nick.McDermaid

101

Anda dapat menggunakan Invoke-Sqlcmdcmdlet

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

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


22
Seseorang harus menyebutkan ini mungkin bagus jika Anda berada dalam konteks server sql, tetapi tidak terlalu banyak jika Anda menggunakan workstation Anda ...
aikeru

10
Anda dapat menjalankan ini di mana saja alat klien SQL Server (SSMS) diinstal. Ini berfungsi dengan baik dari workstation apa pun, apakah itu menjalankan SQL Server atau tidak.
alroc

3
Gunakan impor berikut untuk memiliki cmdlet yang tersedia: Import-Module "sqlps" -DisableNameChecking
xx1xx

1
Jika Anda masih menggunakan SQL 2008 R2, Anda perlu menggunakan modul work sekitar: sev17.com/2010/07/10/making-a-sqlps-module
Vincent De Smet

2
Invoke-SqlCmd adalah mimpi buruk tanpa akhir dari kasus tepi yang aneh dan perilaku yang tidak konsisten. Mengapa terkadang menghasilkan kolom dan bukan waktu lainnya? Di mana pesan kesalahan saya? Mengapa ada di satu komputer atau tidak? Bagaimana cara menginstalnya? Jawaban untuk setiap pertanyaan lebih buruk daripada yang terakhir.
Pxtl

28

Inilah contoh yang saya temukan di blog ini .

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

Agaknya Anda bisa mengganti pernyataan TSQL yang berbeda di mana katanya dbcc freeproccache.


1
Solusi ini bekerja untuk saya, bagaimanapun, ExecuteNonQuery()kembali nol pada keberhasilan, kondisi yang saya gunakan adalah: if ($cmd.ExecuteNonQuery() -ne 0).
Gixabel

Tampaknya mengembalikan jumlah baris yang terkena dampak. docs.microsoft.com/en-us/dotnet/api/…
NicolasW

27

Fungsi ini akan mengembalikan hasil kueri sebagai array objek PowerShell sehingga Anda dapat menggunakannya dalam filter dan mengakses kolom dengan mudah:

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
}

Mengapa ini lebih disukai daripada mengisi DataTable(lihat jawaban Adam )?
alroc

2
Mungkin tidak ada perbedaan besar, tetapi SqlDataReaders umumnya lebih disukai karena mereka mengkonsumsi lebih sedikit sumber daya. Itu tidak mungkin relevan di sini, tetapi menyenangkan untuk mendapatkan kembali objek nyata alih-alih data yang dapat Anda gunakan di foreach dan di mana klausa tanpa khawatir tentang sumber data.
mcobrien

1
contoh penggunaan akan menyenangkan.
Eric Schneider

Terkadang tidak ada cukup bintang
Fred B

13

Jika Anda ingin melakukannya di mesin lokal Anda daripada dalam konteks SQL server maka saya akan menggunakan yang berikut ini. Itu yang kami gunakan di perusahaan saya.

$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

Cukup isi $ ServerName , $ DatabaseName dan $ Query variabel dan Anda harus melakukannya dengan baik.

Saya tidak yakin bagaimana kami menemukan ini pada awalnya, tetapi ada sesuatu yang sangat mirip di sini .


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

itu akan menunjukkan jumlah koneksi dalam sql yang digunakan oleh perintah powershell
arnav


0

Untuk menghindari SQL Injection dengan parameter varchar yang bisa Anda gunakan


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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.