Làm thế nào để bạn chạy một truy vấn SQL Server từ PowerShell?


161

Có cách nào để thực hiện một truy vấn tùy ý trên Máy chủ SQL bằng Powershell trên máy cục bộ của tôi không?

Câu trả lời:


166

Đối với những người khác cần làm điều này chỉ với stock .net và PowerShell (không có công cụ SQL bổ sung nào được cài đặt) ở đây là chức năng mà tôi sử dụng:

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

}

Tôi đã sử dụng nó rất lâu Tôi không biết ai đã viết phần nào nhưng phần này được chắt lọc từ các ví dụ khác nhưng được đơn giản hóa để rõ ràng và chỉ những gì cần thiết mà không cần phụ thuộc hoặc tính năng bổ sung.

Tôi sử dụng và chia sẻ điều này thường xuyên đến mức tôi đã biến nó thành một mô-đun kịch bản trên GitHub để bây giờ bạn có thể đi đến thư mục mô-đun của mình và thực thi git clone https://github.com/ChrisMagnuson/InvokeSQLvà từ đó, invoke-sql sẽ tự động được tải khi bạn sử dụng nó (giả sử sử dụng powershell v3 trở lên).


xử lý không quan trọng ở đây hoặc trong powershell?
Maslow

2
@Maslow Tôi không thể nói chắc chắn, tôi biết rằng điều này hoạt động tốt mà không cần xử lý các đối tượng nhưng nếu bạn có một quy trình powershell.exe sẽ gọi điều này nhiều lần trong nhiều tuần mà không đóng thì cuối cùng có thể là một vấn đề nhưng bạn sẽ phải kiểm tra điều đó
Chris Magnuson

1
Lưu ý rằng điều này buộc bạn phải viết các tập lệnh có thể dễ bị tấn công sql, nếu chúng phụ thuộc vào việc đọc dữ liệu cho truy vấn từ một nguồn dựa vào đầu vào của người dùng.
Joel Coehoorn

4
@ ALLTradesJack Google Sql tiêm. Lệnh Invoke-Sql không có cách bao gồm các tham số tách biệt với văn bản lệnh. Điều này khá nhiều đảm bảo bạn đã sử dụng nối chuỗi để xây dựng các truy vấn và đó là một điều không nên.
Joel Coehoorn

1
Hoạt động tốt cho tôi. Đối với bất cứ ai tự hỏi, để loại bỏ một đối tượng, chỉ cần thêm $connection.dispose()vv Tôi không biết liệu nó có làm nên sự khác biệt nào không
Nick.McDilyn

101

Bạn có thể sử dụng Invoke-Sqlcmdlệnh ghép ngắn

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

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


22
Ai đó nên đề cập đến điều này có thể là tuyệt vời nếu bạn đang ở trong bối cảnh của máy chủ sql, nhưng không quá nhiều nếu bạn đang sử dụng máy trạm của mình ...
aikeru

10
Bạn có thể chạy nó bất cứ nơi nào các công cụ máy khách SQL Server (SSMS) được cài đặt. Nó hoạt động tốt từ bất kỳ máy trạm nào, cho dù nó có chạy SQL Server hay không.
alroc

3
Sử dụng nhập sau để có sẵn cmdlet: Import-Module "sqlps" -DisableNameChecking
xx1xx

1
Nếu bạn vẫn đang sử dụng SQL 2008 R2, bạn cần sử dụng một mô-đun làm việc xung quanh: Sev17.com/2010/07/10/making-a-sqlps-module
Vincent De Smet

2
Invoke-SqlCmd là cơn ác mộng bất tận của những vụ án kỳ quái và hành vi không nhất quán. Tại sao đôi khi nó xuất ra các cột mà không phải các lần khác? Thông báo lỗi của tôi ở đâu? Tại sao nó trên một máy tính mà không phải một máy tính khác? Làm thế nào để tôi cài đặt nó? Câu trả lời cho mỗi câu hỏi còn tệ hơn câu hỏi cuối cùng.
Pxtl

28

Đây là một ví dụ tôi tìm thấy trên blog này .

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

Có lẽ bạn có thể thay thế một tuyên bố TSQL khác trong đó nói dbcc freeproccache.


1
Tuy nhiên, giải pháp này hiệu quả với tôi, ExecuteNonQuery()không trả về thành công, điều kiện tôi sử dụng là : if ($cmd.ExecuteNonQuery() -ne 0).
Gixabel

Có vẻ như nó trả về số lượng dòng bị ảnh hưởng. docs.microsoft.com/en-us/dotnet/api/ Kẻ
NicolasW

27

Hàm này sẽ trả về kết quả của một truy vấn dưới dạng một mảng các đối tượng powershell để bạn có thể sử dụng chúng trong các bộ lọc và truy cập các cột một cách dễ dàng:

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
}

Tại sao điều này tốt hơn là điền vào một DataTable(xem câu trả lời của Adam )?
alroc

2
Có lẽ không có sự khác biệt quá lớn, nhưng SqlDataReaders thường được ưa thích hơn vì chúng tiêu thụ ít tài nguyên hơn. Điều đó dường như không liên quan ở đây nhưng thật tuyệt khi lấy lại các đối tượng thực sự thay vì một dữ liệu mà bạn có thể sử dụng trong foreach và các mệnh đề mà không phải lo lắng về nguồn dữ liệu.
mcobrien

1
một ví dụ sử dụng sẽ tốt đẹp.
Eric Schneider

Đôi khi không có đủ sao
Fred B

13

Nếu bạn muốn làm điều đó trên máy cục bộ của bạn thay vì trong ngữ cảnh của máy chủ SQL thì tôi sẽ sử dụng như sau. Đó là những gì chúng tôi sử dụng tại công ty của tôi.

$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

Chỉ cần điền vào các biến $ ServerName , $ DatabaseName$ Query và bạn sẽ thấy ổn.

Tôi không chắc làm thế nào ban đầu chúng tôi phát hiện ra điều này, nhưng có một cái gì đó rất giống ở đây .


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

nó sẽ hiển thị số lượng kết nối trong sql được sử dụng bởi lệnh powershell
arnav


0

Để tránh SQL Injection với các tham số varchar bạn có thể sử dụng


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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.