Comment exécuter une requête SQL Server à partir de PowerShell?

161

Existe-t-il un moyen d'exécuter une requête arbitraire sur un serveur SQL à l'aide de Powershell sur ma machine locale?


la source

Réponses:

166

Pour les autres qui ont besoin de le faire avec juste du stock .net et PowerShell (aucun outil SQL supplémentaire installé), voici la fonction que j'utilise:

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

}

J'utilise ceci depuis si longtemps que je ne sais pas qui a écrit quelles parties, mais cela a été distillé à partir d'exemples d'autres mais simplifié pour être clair et juste ce qui est nécessaire sans dépendances ou fonctionnalités supplémentaires.

J'utilise et partage cela assez souvent pour que je l'ai transformé en un module de script sur GitHub afin que vous puissiez maintenant aller dans votre répertoire de modules et l'exécuter git clone https://github.com/ChrisMagnuson/InvokeSQLet à partir de ce moment, invoke-sql sera automatiquement chargé lorsque vous l'utiliserez (en supposant votre utilisation de PowerShell v3 ou version ultérieure).

Chris Magnuson
la source
est-ce que l'élimination n'a pas d'importance ici ou dans PowerShell?
Maslow
2
@Maslow Je ne pourrais pas dire avec certitude, je sais que cela fonctionne bien sans éliminer les objets, mais si vous avez un seul processus powershell.exe qui l'appellera plusieurs fois au cours des semaines sans fermer, cela pourrait éventuellement être un problème, mais vous aurait à tester cela.
Chris Magnuson
1
Notez que cela vous oblige à écrire des scripts qui peuvent être vulnérables aux attaques par injection SQL, s'ils dépendent de la lecture des données de la requête à partir d'une source qui repose sur l'entrée utilisateur.
Joel Coehoorn
4
@AllTradesJack Google Sql Injection. La commande Invoke-Sql ne dispose pas d'un moyen d'inclure des paramètres séparés du texte de la commande. Cela garantit à peu près que vous avez utilisé la concaténation de chaînes pour générer les requêtes, et c'est un grand non-non.
Joel Coehoorn le
1
Fonctionne bien pour moi. Pour ceux qui se demandent, pour éliminer un objet, ajoutez simplement $connection.dispose()etc. Je ne sais pas si cela fait une différence
Nick.McDermaid
101

Vous pouvez utiliser l' Invoke-Sqlcmdapplet de commande

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

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

manojlds
la source
22
Quelqu'un devrait mentionner que cela peut être génial si vous êtes dans le contexte du serveur SQL, mais pas tellement si vous utilisez votre poste de travail ...
aikeru
10
Vous pouvez l'exécuter partout où les outils client SQL Server (SSMS) sont installés. Cela fonctionne bien à partir de n'importe quel poste de travail, qu'il exécute ou non SQL Server.
alroc
3
Utilisez l'importation suivante pour que l'applet de commande soit disponible: Import-Module "sqlps" -DisableNameChecking
xx1xx
1
Si vous êtes toujours sur SQL 2008 R2, vous devez utiliser un module de contournement: sev17.com/2010/07/10/making-a-sqlps-module
Vincent De Smet
2
Invoke-SqlCmd est un cauchemar sans fin de cas extrêmes bizarres et de comportements incohérents. Pourquoi génère-t-il parfois des colonnes et pas d'autres fois? Où sont mes messages d'erreur? Pourquoi est-ce sur un ordinateur ou pas sur un autre? Comment l'installer? La réponse à chaque question est pire que la précédente.
Pxtl
28

Voici un exemple que j'ai trouvé sur ce blog .

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

Vraisemblablement, vous pouvez remplacer une instruction TSQL différente là où elle est indiquée dbcc freeproccache.

dmc
la source
1
Cependant, cette solution a fonctionné pour moi, est ExecuteNonQuery()revenu à zéro en cas de succès, à condition que je utiliser est: if ($cmd.ExecuteNonQuery() -ne 0).
Gixabel
Il semble qu'il renvoie le nombre de lignes impactées. docs.microsoft.com/en-us/dotnet/api/…
NicolasW
27

Cette fonction retournera les résultats d'une requête sous forme de tableau d'objets PowerShell afin que vous puissiez les utiliser dans des filtres et accéder facilement aux colonnes:

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
}
mcobrien
la source
Pourquoi est-ce préférable de remplir un DataTable(voir la réponse d'Adam )?
alroc
2
Il n'y a probablement pas une énorme différence, mais les SqlDataReaders sont généralement préférés car ils consomment moins de ressources. Ce n'est probablement pas pertinent ici, mais il est bien de récupérer des objets réels au lieu d'une table de données que vous pouvez utiliser dans des clauses foreach et where sans vous soucier de la source des données.
mcobrien
1
un exemple d'utilisation serait bien.
Eric Schneider
Parfois, il n'y a pas assez d'étoiles
Fred B
13

Si vous voulez le faire sur votre machine locale au lieu de dans le contexte du serveur SQL, j'utiliserais ce qui suit. C'est ce que nous utilisons dans mon entreprise.

$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

Remplissez simplement $ ServerName , $ DatabaseName et $ Query variables et vous devriez être prêt à partir.

Je ne sais pas comment nous l'avons découvert à l'origine, mais il y a quelque chose de très similaire ici .

Adam
la source
12
Invoke-Sqlcmd -Query "sp_who" -ServerInstance . -QueryTimeout 3
Arnav
la source
il affichera le nombre de connexions en sql utilisées par la commande powershell
arnav
0

Pour éviter l'injection SQL avec des paramètres varchar, vous pouvez utiliser


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
Piero
la source