Как вы выполняете запрос 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?
— Маслоу

2
@Maslow Я не могу сказать наверняка, я знаю, что это нормально работает без удаления объектов, но если у вас есть один процесс powershell.exe, который будет вызывать его несколько раз в течение нескольких недель без закрытия, то это может в конечном итоге стать проблемой, но вы придется проверить это.
— Крис Магнусон

1
Обратите внимание, что это вынуждает вас писать сценарии, которые могут быть уязвимы для атак внедрения SQL, если они зависят от чтения данных для запроса из источника, основанного на вводе пользователем.
— Джоэл Коухорн

4
@AllTradesJack Google Sql Injection. Команда 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 или нет.
— 13

3
Используйте следующий импорт, чтобы иметь доступ к командлету: Import-Module "sqlps" -DisableNameChecking
— xx1xx

1
Если вы все еще используете SQL 2008 R2, вам нужно использовать обходной
— Винсент Де Смет

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

Эта функция возвращает результаты запроса в виде массива объектов 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 обычно предпочитают, потому что они потребляют меньше ресурсов. Это вряд ли уместно здесь, но было бы неплохо вернуть реальные объекты вместо данных, которые можно использовать в выражениях foreach и where, не беспокоясь об источнике данных.
— Макобриен

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.