从t-sql代码中删除xp_cmdshell调用的最佳方法是什么

时间:2016-04-04 08:07:40

标签: sql-server tsql powershell bcp sqlps

我维护大型基于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。

感谢您的任何建议。

2 个答案:

答案 0 :(得分:0)

您的数据导出选项如下:

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

HTH

答案 1 :(得分:0)

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

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

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

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

要调用不带xp_cmdshell的脚本,您应该使用powershell步骤创建一个作业,并在t-sql中运行它。