【问题标题】:How do you run a SQL Server query from PowerShell?如何从 PowerShell 运行 SQL Server 查询?
【发布时间】:2012-01-15 10:47:47
【问题描述】:

有没有办法在我的本地机器上使用 Powershell 在 SQL Server 上执行任意查询?

【问题讨论】:

    标签: powershell sql


    【解决方案1】:

    对于其他需要仅使用库存 .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 或更高版本)。

    【讨论】:

    • @Maslow 我不能肯定地说,我知道这在不处理对象的情况下可以正常工作,但是如果你有一个 powershell.exe 进程会在几周内多次调用它而不关闭然后这最终可能会成为一个问题,但您必须对其进行测试。
    • 请注意,如果脚本依赖于从依赖于用户输入的来源读取查询数据,这将迫使您编写可能容易受到 sql 注入攻击的脚本。
    • @JoelCoehoorn 你能为我们中间的外行解释更多吗?
    • @AllTradesJack Google Sql 注入。 Invoke-Sql 命令无法包含与命令文本分开的参数。这几乎可以保证您使用字符串连接来构建查询,这是一个很大的禁忌。
    • 对我来说效果很好。对于任何想处理对象的人,只需添加 $connection.dispose() 等。我不知道它是否有任何区别
    【解决方案2】:

    您可以使用Invoke-Sqlcmd cmdlet

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

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

    【讨论】:

    • 有人应该提到,如果您在 sql server 的上下文中,这可能会很棒,但如果您使用的是工作站,那就不是这样了......
    • 您可以在安装了 SQL Server 客户端工具 (SSMS) 的任何位置运行此程序。它可以在任何工作站上正常工作,无论它是否运行 SQL Server。
    • 使用以下导入使 cmdlet 可用:Import-Module "sqlps" -DisableNameChecking
    • 如果您仍在使用 SQL 2008 R2,则需要使用变通模块:sev17.com/2010/07/10/making-a-sqlps-module
    • Invoke-SqlCmd 是奇异边缘情况和不一致行为的无尽噩梦。为什么它有时输出列而不是其他时间?我的错误信息在哪里?为什么它在一台计算机上或不在另一台计算机上?我该如何安装它?每个问题的答案都比上一个差。
    【解决方案3】:

    此函数会将查询结果作为 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(见Adam's answer)更可取?
    • 可能没有太大的区别,但通常首选 SqlDataReader,因为它们消耗的资源较少。这在这里不太可能相关,但是很高兴取回真实的对象,而不是可以在 foreach 和 where 子句中使用的数据表,而不必担心数据的来源。
    • 一个示例用法会很好。
    • 有时星星不够
    【解决方案4】:

    这是我在 this 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();
    

    大概你可以用一个不同的 TSQL 语句替换它说dbcc freeproccache

    【讨论】:

    • 这个解决方案对我有用,但是,ExecuteNonQuery() 成功返回零,我使用的条件是:if ($cmd.ExecuteNonQuery() -ne 0)
    • 似乎它返回受影响的行数。 docs.microsoft.com/en-us/dotnet/api/…
    【解决方案5】:

    如果您想在 本地计算机 上而不是在 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 变量即可。

    我不确定我们最初是如何发现这一点的,但有一些非常相似的here

    【讨论】:

      【解决方案6】:
      Invoke-Sqlcmd -Query "sp_who" -ServerInstance . -QueryTimeout 3
      

      【讨论】:

      • 它将显示powershell命令使用的sql中的连接数
      【解决方案7】:

      没有运行 SQL 查询的内置“PowerShell”方式。如果您有SQL Server tools installed,您将获得一个Invoke-SqlCmd cmdlet。

      由于 PowerShell 是基于 .NET 构建的,因此您可以使用 ADO.NET API 来运行您的查询。

      【讨论】:

        【解决方案8】:

        为了避免使用 varchar 参数的 SQL 注入,您可以使用

        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 = "connectionstringHere"
        $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
        

        【讨论】:

        • 我无法想象 PS 脚本易受 SQL 注入影响的场景,但我更喜欢手动构建查询。我认为它更具可读性。谢谢!
        【解决方案9】:

        您甚至可以根据需要格式化字符串并传递参数。

        case "ADDSQLSERVERUSER":
            //0 = coprorateName;
            //1 = user password
            //2 = servername
            command = @"$sqlQuery = Use JazzUWS_'{0}' 
                        Create login UWSUser_'{0}' with password='{1}';
                        Create user UWSUser_'{0}' for login UWSUser_'{0}';
                        Grant Execute to UWSUser_'{0}';
        
                        Use ReportSvrUWS_'{0}' 
                        Create user UWSUser_'{0}' for login UWSUser_'{0}';
                        Grant Execute to UWSUser_'{0}';
        
                        Invoke-Sqlcmd -Query $sqlQuery -ServerInstance '{2}'";
            break;
        

        用于远程执行的 C# 代码(您可以组织自己的方式)

                string script = PowershellDictionary.GetPowershellCommand("ADDSQLSERVERUSER");
                script = String.Format(script, this.CorporateName, password, this.SQLServerName)
                PowerShellExecution.RunScriptRemote(_credentials.Server, _credentials.Username, _credentials.Password, new List<string> { script });
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多