คุณเรียกใช้แบบสอบถาม SQL Server จาก PowerShell ได้อย่างไร


161

มีวิธีการดำเนินการสอบถามโดยพลการบน SQL Server โดยใช้ Powershell บนเครื่องของฉันหรือไม่?

คำตอบ:


166

สำหรับคนอื่น ๆ ที่ต้องการทำสิ่งนี้ด้วยหุ้น. net และ PowerShell (ไม่ได้ติดตั้งเครื่องมือ SQL เพิ่มเติม) นี่คือฟังก์ชันที่ฉันใช้:

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

}

ฉันใช้มันมานานแล้วฉันไม่รู้ว่าใครเป็นคนเขียนส่วนใด แต่สิ่งนี้กลั่นจากตัวอย่างของคนอื่น แต่ทำให้เข้าใจง่ายและสิ่งที่ต้องการโดยไม่มีการพึ่งพาหรือคุณลักษณะพิเศษเพิ่มเติม

ฉันใช้และแบ่งปันสิ่งนี้บ่อยพอที่จะทำให้สิ่งนี้กลายเป็นโมดูลสคริปต์บนGitHubเพื่อให้คุณสามารถไปที่ไดเรกทอรีโมดูลของคุณและดำเนินการgit clone https://github.com/ChrisMagnuson/InvokeSQLและจากจุดนั้นไปข้างหน้า invoke-sql จะถูกโหลดโดยอัตโนมัติเมื่อคุณใช้งาน (สมมติว่า การใช้ powershell v3 ของคุณหรือใหม่กว่า)


การกำจัดไม่สำคัญที่นี่หรือใน PowerShell?
Maslow

2
@ ช้าฉันไม่สามารถพูดได้อย่างแน่นอนฉันรู้ว่ามันทำงานได้ดีโดยไม่ต้องกำจัดวัตถุ แต่ถ้าคุณมีกระบวนการ powershell.exe เดียวที่จะเรียกสิ่งนี้หลายครั้งในช่วงสัปดาห์โดยไม่ต้องปิดมันอาจเป็นปัญหา แต่ในที่สุดคุณ จะต้องทดสอบว่า
Chris Magnuson

1
โปรดทราบว่าสิ่งนี้บังคับให้คุณเขียนสคริปต์ที่อาจเสี่ยงต่อการถูกโจมตีแบบ sql ถ้าพวกเขาขึ้นอยู่กับการอ่านข้อมูลสำหรับการสืบค้นจากแหล่งข้อมูลที่อาศัยการป้อนข้อมูลของผู้ใช้
Joel Coehoorn

4
@AllTradesJack Google Sql Injection คำสั่ง Invoke-Sql ไม่มีวิธีรวมพารามิเตอร์ที่แยกจากข้อความคำสั่ง สิ่งนี้รับประกันได้เลยว่าคุณใช้การต่อสายสตริงเพื่อสร้างคิวรี
Joel Coehoorn

1
ทำงานได้ดีสำหรับฉัน สำหรับใครที่สงสัยว่าจะกำจัดวัตถุเพียงแค่เพิ่ม$connection.dispose()เป็นต้นฉันไม่รู้ว่ามันสร้างความแตกต่างหรือไม่
Nick.McDermaid

101

คุณสามารถใช้Invoke-Sqlcmdcmdlet

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

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


22
ใครบางคนควรจะกล่าวถึงนี้อาจจะดีถ้าคุณอยู่ในบริบทของเซิร์ฟเวอร์ SQL แต่ไม่มากถ้าคุณกำลังใช้เวิร์กสเตชันของคุณ ...
aikeru

10
คุณสามารถเรียกใช้สิ่งนี้ได้ทุกที่ที่ติดตั้งเครื่องมือไคลเอ็นต์ SQL Server (SSMS) มันทำงานได้ดีจากเวิร์กสเตชันใด ๆ ไม่ว่าจะใช้ SQL Server หรือไม่ก็ตาม
alroc

3
ใช้การอิมพอร์ตต่อไปนี้เพื่อให้ cmdlet พร้อมใช้งาน: Import-Module "sqlps" -DisableNameChecking
xx1xx

1
หากคุณยังอยู่ใน SQL 2008 R2 คุณต้องใช้งานโมดูล: sev17.com/2010/07/10/making-a-sqlps-module
Vincent De Smet

2
Invoke-SqlCmd เป็นฝันร้ายไม่รู้จบของคดีขอบแปลกและพฤติกรรมที่ไม่สอดคล้องกัน เหตุใดบางครั้งมันจึงแสดงผลคอลัมน์ไม่ใช่เวลาอื่น ข้อความแสดงข้อผิดพลาดของฉันอยู่ที่ไหน เหตุใดจึงมีคอมพิวเตอร์เครื่องหนึ่งหรือไม่ใช่เครื่องอื่น ฉันจะติดตั้งได้อย่างไร คำตอบของคำถามแต่ละข้อนั้นแย่กว่าคำถามสุดท้าย
Pxtl

28

นี่คือตัวอย่างที่ฉันพบในบล็อกนี้

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

สมมุติคุณสามารถทดแทนคำสั่ง TSQL dbcc freeproccacheที่แตกต่างกันที่จะกล่าวว่า


1
การแก้ปัญหานี้ทำงานให้ฉัน แต่กลับเป็นศูนย์ในความสำเร็จเงื่อนไขที่ว่าผมใช้คือ:ExecuteNonQuery() if ($cmd.ExecuteNonQuery() -ne 0)
Gixabel

ดูเหมือนว่าจะส่งคืนจำนวนบรรทัดที่ได้รับผลกระทบ docs.microsoft.com/en-us/dotnet/api/…
NicolasW

27

ฟังก์ชันนี้จะส่งคืนผลลัพธ์ของแบบสอบถามเป็นอาร์เรย์ของวัตถุ PowerShell เพื่อให้คุณสามารถใช้พวกมันในตัวกรองและคอลัมน์เข้าถึงได้อย่างง่ายดาย:

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
}

ทำไมสิ่งนี้จึงดีกว่าการเติมDataTable(ดูคำตอบของอดัม )
alroc

2
อาจจะไม่แตกต่างกันมากนัก แต่มักนิยมใช้ SqlDataReaders เพราะใช้ทรัพยากรน้อยกว่า ไม่น่าจะเกี่ยวข้องกันที่นี่ แต่เป็นการดีที่จะเอาออบเจ็กต์จริงกลับมาแทนที่จะเป็นดาต้าดาต้าที่คุณสามารถใช้ในหน้าและสถานที่ที่ข้อได้โดยไม่ต้องกังวลเกี่ยวกับแหล่งข้อมูล
mcobrien

1
ตัวอย่างการใช้จะดี
Eric Schneider

บางครั้งมีไม่มากพอที่ดาว
เฟร็ด B

13

หากคุณต้องการที่จะทำบนเครื่องท้องถิ่นของคุณแทนในบริบทของเซิร์ฟเวอร์ SQL แล้วฉันจะใช้ต่อไปนี้ นี่คือสิ่งที่เราใช้ใน บริษัท ของฉัน

$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

เพียงแค่กรอก$ ServerName , $ DatabaseNameและตัวแปร$ Queryและคุณควรจะดี

ผมไม่แน่ใจว่าวิธีการที่เราพบในขั้นต้นนี้ออก แต่มีบางสิ่งบางอย่างที่คล้ายกันมากที่นี่


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

มันจะแสดงหมายเลขของการเชื่อมต่อใน SQL ใช้โดยคำสั่ง PowerShell
arnav

11

ไม่มี "PowerShell" ในตัวในการเรียกใช้แบบสอบถาม SQL หากคุณติดตั้งเครื่องมือ SQL Serverคุณจะได้รับInvoke-SqlCmd cmdlet

เนื่องจาก PowerShell สร้างบน. NET คุณสามารถใช้ADO.NET APIเพื่อเรียกใช้คิวรีของคุณ


0

เพื่อหลีกเลี่ยง SQL Injection ด้วยพารามิเตอร์ varchar คุณสามารถใช้


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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.