【问题标题】:What is the best to remove xp_cmdshell calls from t-sql code从 t-sql 代码中删除 xp_cmdshell 调用的最佳方法是什么
【发布时间】:2016-07-23 16:28:06
【问题描述】:

我正在维护基于大型 t-sql 的应用程序。 它有很多通过xp_cmdshell调用的bcp的用法。

这是有问题的,因为 xp_cmdshell 具有与 SQL Server 服务帐户相同的安全上下文,并且对于工作来说是非常必要的。

我摆脱这个缺点的第一个想法是使用 CLR 代码。 CLR 以调用代码的用户的权限运行。 我创建了以下程序,它工作正常。我可以看到它正在使用运行此代码的帐户的权限:

public static void RunBCP(SqlString arguments, out SqlString output_msg, out SqlString error_msg, out SqlInt32 return_val) {
        output_msg = string.Empty;
        error_msg = string.Empty;
        try {
            var proc = new Process {
                StartInfo = new ProcessStartInfo {
                    FileName = "bcp",
                    Arguments = arguments.ToString(),
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    CreateNoWindow = true
                }
            };
            proc.Start();
            while (!proc.StandardOutput.EndOfStream) {
                output_msg += proc.StandardOutput.ReadLine();
            }
            return_val = proc.ExitCode;
        }
        catch (Exception e) {
            error_msg = e.Message;
            return_val = 1;
        }
    }

这是一个很好的解决方案,因为我不会在 BCP 调用中搞砸(参数相同)。逻辑上没有重大变化,因此没有出错的风险。

因此,之前在 T-SQL 中调用 BCP 是这样的:

declare @ReturnCode int;
declare @cmd varchar(1000);
SELECT @CMD = 'bcp "select FirstName, LastName, DateOfBirth"  queryout "c:\temp\OutputFile.csv" -c -t -T -S"(local)"'
EXEC @ReturnCode=xp_cmdshell @CMD,no_output

现在我这样称呼它:

declare @ReturnCode int;
declare @cmd varchar(1000);
SELECT @CMD = '"select FirstName, LastName, DateOfBirth"  queryout "c:\temp\OutputFile.csv" -c -t -T -S"(local)"'
exec DataBase.dbo.up_RunBCP @arguments = @cmd;

所以,问题是:有没有其他方法可以摆脱 xp_cmdshell bcp 代码? 我听说我可以使用 PowerShell(sqlps)。但我发现的示例建议创建一个 powershell 脚本。 我可以从 t-sql 代码中调用这样的脚本吗? 应该如何存储此代码(powershell 脚本)?作为数据库对象? 或者也许还有其他方法?不需要 SSIS。我最想知道的是关于 powershell。

感谢您的任何建议。

【问题讨论】:

    标签: sql-server tsql powershell bcp sqlps


    【解决方案1】:

    您的数据导出选项如下:

    • 使用 xp_cmdshell 调用 bcp.exe - 大容量复制的旧方法
    • 使用 CLR - 批量复制的新方法
    • SSIS - 我的首选方式; here is the example
    • INSERT INTO OPENROWSET - 如果您在 32 位环境中工作并安装了 text/Jet/任何驱动程序,或者您可以安装 64 位驱动程序(例如 64 位 ODBC 文本驱动程序,请参阅Microsoft Access 数据库引擎 2010 可再发行组件)
    • SQL Server 导入/导出向导 - 丑陋的手动方式,很少按照您希望的方式工作
    • 使用外部 CSV 表 - 尚不支持(SQL Server 2016 承诺将...)

    HTH

    【讨论】:

      【解决方案2】:

      我会使用简单的 Powershell 脚本来执行此操作,例如:

      Invoke-SqlCommand -query '...' | ExportTo-Csv ...
      

      通常,对于管理功能,您可以将其添加到任务计划程序并完成它。如果您需要根据需要执行此任务,您可以通过xp_cmdshell 使用schtasks.exe run Task_NAME 来执行此任务,这可能对您更好,因为在给定的上下文中,在 Powershell 中表达自己比在 T-SQL 中表达自己可能更容易。

      其他提到的东西都需要额外的工具(例如 SSIS 需要 VS),这是可移植的,没有依赖关系。

      要在没有xp_cmdshell 的情况下调用脚本,您应该使用 powershell 步骤创建一个作业并从 t-sql 中运行它。

      【讨论】:

      • 这不是管理任务。它是工作流程的一部分。我可以在 t-sql 代码中调用 PowerShell 脚本吗?
      • 当然...xp_cmdshell Powershell -File script.ps1。同样来自代理内部:msdn.microsoft.com/en-us/library/hh213688.aspx
      • 所以这不是我想要的方式。我需要摆脱 xp_cmdshell。
      • 你可以从 withing 作业中使用它,然后从 tsql 中触发作业,所以它可以在没有 xp_cmdshell 的情况下使用。我编辑了答案。
      猜你喜欢
      • 2011-02-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-30
      • 1970-01-01
      • 1970-01-01
      • 2021-12-16
      相关资源
      最近更新 更多