如何将存储过程返回的多记录查询结果输出到TXT文本文件
我有一个简单查询,用于从指定日期的记录中拉取符合特定名称和状态的所有记录ID,输出仅需要ID,优先为逗号分隔的单行文本,每条记录单独占一行的txt文件也可接受。
以下是我拉取所需数据的查询(在SSMS中操作SAP B1数据):
SELECT T0.[DocNum] FROM OINV T0 WHERE T0.[DocDate] = CAST(CURRENT_TIMESTAMP AS DATE) AND T0.CardName LIKE '%Name%' ORDER BY T0.CardName
我将该查询创建为存储过程想要定时执行,但无法实现查询返回多条记录时导出到.txt文件的需求。
我先尝试用SQLCMD在查询前加:OUT C:\file.txt的方式输出文件,但该方法无法在存储过程中使用。
之后我尝试在存储过程中调用xp_cmdshell写入文件,但返回多条记录时就会报错,存储过程代码如下:
SET NOCOUNT ON; DECLARE @timeStamp varchar(200) = convert(varchar,getDate(), 112 )+'_'+ Replace(convert(varchar,getDate(), 114 ),':','') DECLARE @var NVARCHAR(MAX) = '' DECLARE @fn varchar(500) = 'C:\file.txt'; DECLARE @cmd varchar(8000) SET @var = (SELECT T0.[DocNum] FROM OINV T0 WHERE T0.[DocDate] = CAST(CURRENT_TIMESTAMP AS DATE) AND T0.CardName like '%Name%' ORDER BY T0.CardName) SET @cmd = concat('echo ', @var, ' > "', @fn, '"'); EXEC xp_cmdshell @cmd;
查询返回多条记录时报错信息如下:
子查询返回多个值。当子查询跟随=、!=、<、<=、>、>= 或用作表达式时,不允许返回多个值。
我也了解过bcp方案,但一开始无法在存储过程中成功运行,部分解答建议用TOP 1或WHERE条件限制仅返回单条记录,但我需要保存所有查询结果,因此这类方案不可行。
想请问将存储过程的多记录查询结果输出到文本文件的最简单方法是什么?
补充说明1
感谢各位的建议,以下是我尝试过的其他方案及遇到的问题:
- 使用BCP
我尝试了以下两种BCP实现方式,都报错“存储过程需要varchar类型的'command_string'参数”:
实现1:
DECLARE @strbcpcmd NVARCHAR(max) SET @strbcpcmd = 'bcp "SELECT T0.[DocNum] FROM OINV T0 WHERE T0.[DocDate] = CAST(CURRENT_TIMESTAMP AS DATE) AND T0.CardName like ''%Name%''" queryout "C:\test.txt" -w -C OEM -t"$" -T -S'+@@servername EXEC master..xp_cmdshell @strbcpcmd
实现2:
DECLARE @bcp nvarchar(max) DECLARE @timeStamp varchar(200) = convert(varchar,getDate(), 112 )+'_'+ Replace(convert(varchar,getDate(), 114 ),':','') SET @bcp='bcp "SELECT T0.[DocNum] FROM OINV T0 WHERE T0.[DocDate] = CAST(CURRENT_TIMESTAMP AS DATE) AND T0.CardName like ''%Name%''" queryout "C:\Test.txt" -c -t, -ServerName01 -T' EXEC master.dbo.xp_cmdshell @bcp, no_output /*可选,调试时可删除no_output */
我需要文件名随运行时间动态生成,因此希望尽可能保留CURRENT_TIMESTAMP AS DATE的逻辑。
补充说明2
我将变量类型改为DECLARE @bcp nvarchar(1000)后BCP可以运行,但查询本身报错(该查询单独运行是正常的):
Error = [Microsoft][ODBC Driver 13 for SQL Server][SQL Server]Invalid object name 'OINV'.
以及:
Error = [Microsoft][ODBC Driver 13 for SQL Server]Unable to resolve column level collations
- 使用SQL作业代理
该方案我不太清楚怎么分离查询和输出指令,我尝试先创建包含查询的存储过程,再创建对应作业,额外添加类型为“Operating system (CmdExec)”的步骤,命令为:OUT C:\SQLOut\Test.txt。
我也尝试在同一个步骤中放入完整查询和:OUT C:\SQLOut\Test.txt命令,报错如下:
以用户: NT Service\SQLSERVERAGENT身份执行。作业0x2A9232A6AF3E4F4B8C53CCF50419245D的步骤1无法创建进程(原因:系统找不到指定文件)。步骤失败。
我推测是文件不存在导致的报错,我需要让程序自动生成带时间戳的文件名,不确定该方案是否支持该需求。
- 使用SSIS
我没有相关使用经验可以学习,但当前环境没有安装BIDS,需要额外部署相关组件,对于这个需求来说成本过高。 - 使用STRING_AGG
该方案非常合适,但我们使用的是SQL Server 2016,刚好不支持该功能。 - 使用PowerShell
该方案可以实现需求,但需要用任务计划程序定时执行,我认为该方案不够规范。
最终更新
我最终用BCP实现了需求,BCP方案仅需要对原有代码做少量修改,我之前的问题主要是语法错误,最终可运行的代码如下:
DECLARE @bcp nvarchar(1000) DECLARE @timeStamp varchar(200) = convert(varchar,getDate(), 112 )+'_'+ Replace(convert(varchar,getDate(), 114 ),':','') SET @bcp='bcp "SELECT T0.[DocNum] FROM MyDB.DBO.OINV T0 WHERE T0.[DocDate] = CAST(CURRENT_TIMESTAMP AS DATE) AND T0.CardName like ''%Name%''" queryout "C:\SQLOUT\Name' + @timeStamp + '.txt" -c -t, -Sservername -T' EXEC master.dbo.xp_cmdshell @bcp--, no_output /*可选,调试时可删除no_output */
内容的提问来源于stack exchange,提问作者Brady

