我有一个简单的查询,它可以从一个特定的日期提取具有特定名称和状态的所有记录的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
放在查询之前,可以将查询输出到所需的.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”来确保只返回一条记录,但我需要存储此查询的所有结果,因此这些对我来说不是有效的解决方法。
有人能告诉我将存储过程查询结果(多个)输出到文本文件的最简单的方法是什么吗?
编辑-
感谢大家到目前为止提供的所有建议和意见。我将列出我尝试过的所有其他解决方案,以及我遇到的问题-
*使用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 /*optional, remove no_output for debugging */
这里我想指出的是,文件名最好是动态的,这取决于运行时,这就是为什么我想在那里保持CURRENT_TIMESTAMP AS DATE
,如果可以的话。
**编辑X2 -**我可以通过更改变量类型DECLARE @bcp nvarchar(1000)
来使bcp“工作”,但现在看起来查询本身失败了(尽管它在此之外肯定工作)-
错误= [Microsoft][用于SQL Server的ODBC驱动程序13][SQL Server]对象名称“OINV”无效。
&
错误= [Microsoft][ODBC Driver 13 for SQL Server]无法解析列级排序规则
*使用SQL作业代理
这让我有点困惑,因为我不确定在哪里将查询与输出指令分开。我尝试过的方法是创建包含查询的存储过程,然后基于该存储过程创建一个作业。我添加了一个额外的步骤,即键入“Operating system(CmdExec)”,并将:OUT C:\SQLOut\Test.txt
作为命令。
我还尝试在一个步骤中添加完整的查询沿着:OUT C:\SQLOut\Test.txt
命令。
以用户身份执行:无法为作业0x 2A 9232 A6 AF 3E 4F 4 B8 C53 CCF 50419245 D的步骤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 /*optional, remove no_output for debugging */
4条答案
按热度按时间zvms9eto1#
使用
BCP
非常简单,不需要尝试并 * 存储 * 查询结果。您可以在存储程序中使用的基本
BCP
陈述式如下所示:在上面的BCP语法中,将 servername 替换为SQL Server的DNS名称或IP;-T假定为可信连接,您也可以使用-U和-P指定用户名和密码。
您可以直接在程序中使用此指令,并使用xp_cmdshell执行它,或使用 CmdExec 选项直接从代理程式作业执行它。
要进行调试,您可以直接在SSMS查询窗口中运行它。
pbgvytdp2#
首先,让我说说你所得到的错误。
@var
是具有单一类型nvarchar(max)
的单一变量。您的select语句返回结果集...行和列。结果集中的每个元素(某些行和某些列的每个交集)都是具有其自己的数据类型的单一值。您的查询返回一列,但返回多行。因此,您有多个值。您不能将多个值赋给一个变量。就像Excel中的单个单元格不能是Excel中的整列一样。至于将数据从sql输出到文本文件,纯粹基于TSQL的解决方案并不适合于此。您需要某种“客户端应用程序”来运行SQL,然后与数据库外部的世界交互。
既然您说您希望按计划运行它,那么您可能希望使用SQL代理作为您的“客户端应用程序”,因为它也是一个计划程序。
SQL代理有一个名为“操作系统(CmdExec)"的作业步骤类型。您可以使用此作业步骤类型运行sqlcmd(have a read through this),并让sqlcmd按照您在问题中提到的方式将查询结果输出到文本文件。
您也可以使用SQL Server整合服务套件。如果您已经在某处有整合服务目录,或是您想要进行更多汇出,这是一个特别好的主意。否则,CmdExec解决方案就可以了。
如果选择将SQL代理与CmdExec步骤一起使用,则可能需要设置一个具有有限权限的代理帐户来执行作业。This should get you started。
不要使用
xp_cmdshell
,实际上,usual advice是为了让它完全处于禁用状态。hkmswyz63#
关于您得到的错误...@var只需要一个值,但您试图用返回多行的查询来填充它。
根据您运行的SQL版本,您可以使用STRING_AGG(我相信是在SQL Server 2017中引入的)将结果填充到单个值中,并使用定义的分隔符(在本例中为管道,但您可以将其更改为任何您想要的内容)。
如果你使用的版本不支持STRING_AGG,你可以尝试STUFF:
对不起,我对将它导出到文本文件没有太大帮助。如果是我,我会使用集成服务(SSIS)来完成此操作。
e1xvtsh34#
1.将上面的查询保存到文件,例如:
testBatch.bat
1.创建批处理文件,示例(SQL Hana):
testBatch.bat
个1.在cmd提示符下运行批处理文件:
testBatch.bat > OutputFileTxt.txt
您也可以保存为csv文件:
testBatch.bat > OutputFileTxt.csv
1.结束