Як запустити запит на SQL Server від PowerShell?


161

Чи є спосіб виконати довільний запит на SQL сервері за допомогою 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 або новішої версії).


чи не має значення утилізація тут чи в оболонці?
Маслоу

2
@Maslow Я не можу сказати точно, я знаю, що це прекрасно працює без викидання об'єктів, але якщо у вас є єдиний процес powershell.exe, який буде викликати це кілька разів протягом тижнів, не закриваючи, то, зрештою, це може бути проблемою, але ви доведеться це перевірити.
Кріс Магнусон

1
Зауважте, що це змушує вас писати сценарії, які можуть бути вразливими до атак на введення sql, якщо вони залежать від зчитування даних для запиту з джерела, яке покладається на введення користувача.
Joel Coehoorn

4
@AllTradesJack Інжекція Google Sql. Команда Invoke-Sql не має можливості включати параметри, окремо від тексту команди. Це в значній мірі гарантує, що ви використовували конкатенацію рядків для побудови запитів, і це велика ні-ні.
Джоель Куехорн

1
Добре працює для мене. Для тих, хто цікавиться, як розпоряджатися об’єктом, просто додайте $connection.dispose()і т. Д. Я не знаю, чи має це значення, хоча
Nick.McDermaid

101

Можна використовувати Invoke-Sqlcmdкомандлет

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
Використовуйте наступний імпорт, щоб мати доступ до командлету: 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()повернувся до нуля в разі успіху, умова , що використання I є: if ($cmd.ExecuteNonQuery() -ne 0).
Гіксабель

Здається, він повертає кількість порушених рядків. docs.microsoft.com/en-us/dotnet/api/…
NicolasW

27

Ця функція поверне результати запиту у вигляді масиву об'єктів shellhell, щоб ви могли легко використовувати їх у фільтрах та колонках доступу:

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 зазвичай віддають перевагу, оскільки вони споживають менше ресурсів. Це, мабуть, не стосується тут, але приємно повернути реальні об'єкти замість даних, які можна використовувати в foreach та де клаузи, не турбуючись про джерело даних.
mcobrien

1
приклад використання було б добре.
Ерік Шнайдер

Іноді зірок не вистачає
Фред Б

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


0

Щоб уникнути ін'єкції SQL з параметрами 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.