我正在尝试通过SQL Server 2008上的T-SQL执行一些sqlcmd。我的代码中有一部分用于检查数据文件大小,如果该数据文件大小不等于0,则开始删除具体表,所以我可以在新数据BCP。
以下是我未执行的代码:
SET @myVariable = '
SETLOCAL
FOR %%R IN (X:\Main Folder\Data\'+@databaseName+'_'+@tableName+'.dat) DO SET size=%%~zR
IF %size% NEQ 0 (
SQLCMD -E -S my-server-name -Q "DELETE FROM '+@databaseName+'.'+@schemaName+'.'+@tableName+';" >> X:\Main Folder\Log\Log.txt
)'
EXEC master..xp_cmdshell @myVariable
由于某些原因,当我执行我的存储过程时,上面的代码似乎被跳过,因为它不会回写任何错误消息。
编辑:在重新调整间距和我的代码之后,@ myVariable立即执行。但是,即使数据文件大小= 0,它仍然无法删除表。但是,当我在批处理文件中对其进行硬编码时,它的工作方式非常好。有什么想法吗?答案 0 :(得分:3)
您需要在%
循环中使用单个for
,因为您没有在批处理文件中执行代码(需要%%
),请参阅此post进一步澄清。所以你的for循环应该是:
FOR %R IN (X:\Main Folder\Data\'+@databaseName+'_'+@tableName+'.dat) DO SET size=%~zR
答案 1 :(得分:2)
我不能说你的DOS命令。但我建议使用Ole Automation Procedures
来获取文件大小。这样您就不必依赖于运行批处理命令。
首先,您需要在SQL Server实例上启用Ole Automation Procedures
,如下所示:
sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
sp_configure 'Ole Automation Procedures', 1;
GO
RECONFIGURE;
GO
你只需要这样做一次。
接下来是获取文件大小的脚本。该示例假定有一个名为C:\Temp\testfile.txt
的文件。如果文件存在,脚本将选择大小,如果不存在,则选择0。您可以将此脚本作为示例,根据大小执行您想要的操作。
这里是:
DECLARE @hr INT;
DECLARE @size INT;
DECLARE @obj_file INT;
DECLARE @obj_file_system INT;
DECLARE @file_name VARCHAR(100)='C:\Temp\testfile.txt';
-- Create a FileSystemObject. Create this once for all subsequent file manipulation. Don't forget to destroy this object once you're done with file manipulation (cf cleanup)
EXEC @hr = sp_OACreate 'Scripting.FileSystemObject', @obj_file_system OUT;
IF @hr<>0 GOTO __cleanup;
-- Get a handle for the file. Don't forget to release the handle for each file you get a handle for (see cleanup). The return will be different from 0 if the file doesn't exist
EXEC @hr = sp_OAMethod @obj_file_system, 'GetFile', @obj_file out, @file_name;
IF @hr<>0 GOTO __print_file_size;
-- Retrieve the file size.
EXEC sp_OAGetProperty @obj_file, 'size', @size OUT;
__print_file_size:
SELECT ISNULL(@size,0) AS file_size;
__cleanup:
EXEC sp_OADestroy @obj_file_system;
EXEC sp_OADestroy @obj_file;
答案 2 :(得分:2)
我认为问题在于你没有在文件名周围使用引号。顶级目录中有一个空格。
Jaco的回答看起来是正确的,我确信这是问题的一部分。你应该初始化size
只是为了安全:
SET @myVariable = '
SETLOCAL
SET size=0
FOR %R IN ("X:\Main Folder\Data\'+@databaseName+'_'+@tableName+'.dat") DO SET size=%~zR
IF %size% NEQ 0 (
SQLCMD -E -S my-server-name -Q "DELETE FROM '+@databaseName+'.'+@schemaName+'.'+@tableName+';" >> "X:\Main Folder\Log\Log.txt"
)'
EXEC master..xp_cmdshell @myVariable
如果没有引号,for
循环会将其“set”(for /?
中使用的术语)视为由空格分隔的两个项目。如果您当前的目录是X:\Main Folder\Data\
,它仍然会工作,因为它将最后一个目录视为.dat
文件的相对路径,然后set
在最后一次传递时仍然是正确的值。
答案 3 :(得分:2)
不确定这是否适合您,但由于您使用的是SQL 2008,因此您应该能够使用powershell命令:
DECLARE @File varchar(100), @outputFile varchar(100)
DECLARE @cmd varchar(1000)
SELECT @File = 'path_to_file'
SELECT @outputFile = 'path_to_output_file'
SELECT @cmd = 'powershell.exe -command "if((Get-Item '''+@File+''').length -gt 0) {&sqlcmd.exe -E -S SERVERNAME -Q ''SELECT name FROM master.sys.databases ;'' -o '+@outputFile+'}"'
SELECT @cmd
exec master..xp_cmdshell @cmd
我已经检查过,它似乎正在工作,具体取决于文件大小。
答案 4 :(得分:2)
你为什么要“down”到命令行? (默认情况下有sys.tables WHERE name LIKE ...
被禁用的原因)
你能不能简单地遍历你的表(SELECT INTO
)然后
BULK INSERT
)TRY..CATCH
从(预期)文件进入shadow-table(在DELETE
中处理文件不存在,或者为空或已损坏的情况.. DELETE
实际表记录并移动数据DROP
实际的表记录(如果你假设丢失或空的bcp文件,则保留它们意味着它将在稍后到达并且你仍然坚持当前现在的版本)makeRotationAxis()
影子表答案 5 :(得分:1)
您在代码中使用X:\
。但代码在SQL Server的服务帐户下运行。该帐户可能没有x:
。
我建议使用UNC而不是映射驱动器。此外,请确保您的服务在域帐户下运行,并且域帐户具有UNC的所有必需权限。
答案 6 :(得分:1)
我意识到我可以使用此方法检查表计数而不是数据文件大小:
SET @sqlCheck = 'SQLCMD -E -S ServerA -Q "IF (SELECT COUNT(*) FROM '+@databaseName+'.'+@schemaName+'.'+@tableName+') > 0 BEGIN DELETE FROM ServerB.'+@databaseName+'.'+@schemaName+'.'+@tableName+' END;"'
EXEC MASTER..xp_cmdshell @sqlcheck
答案 7 :(得分:1)
您似乎已经知道了数据库和表的名称,因此您可以使用以下内容,它基本上为您要查找的文件执行DIR并检查它是否为'0 bytes'
,如果是,那么它尽你所能。注意事项:
STRING TEMPLATES - 构建字符串时,我喜欢构建一个“模板”,然后在字符串中替换。这是确保你有正确数量的引号,括号等的好方法。我在这里做了两次,一次构建DIR命令,然后再次构建TRUNCATE命令。
TRUNCATE - 虽然不是您的问题的一部分,但您可能想要使用TRUNCATE
代替DELETE FROM
。如果您的表中有一百万行,DELETE FROM
可能需要2分钟才能运行,而TRUNCATE
总是需要0秒才能运行。
你的回答:
SET NOCOUNT ON;
DECLARE @DatabaseName VARCHAR(50) = 'database1'
DECLARE @TableName VARCHAR(50) = 'table1'
DECLARE @PathTemplate VARCHAR(50) = 'dir c:\temp\{@DatabaseName}_{@TableName}.txt'
SET @PathTemplate = REPLACE(@PathTemplate, '{@DatabaseName}', @DatabaseName);
SET @PathTemplate = REPLACE(@PathTemplate, '{@TableName}', @TableName);
DECLARE @FileNames AS TABLE (FileNames VARCHAR(100))
INSERT @FileNames (FileNames)
exec xp_cmdshell @PathTemplate
IF EXISTS ( SELECT 1 FROM @FileNames WHERE FileNames LIKE '%0 bytes')
BEGIN
PRINT 'No Content/Missing File'
END
ELSE BEGIN
DECLARE @SqlExc VARCHAR(500) = 'TRUNCATE TABLE [{@DatabaseName}].[dbo].[{@TableName}]'
SET @SqlExc = REPLACE(@SqlExc, '{@DatabaseName}', @DatabaseName);
SET @SqlExc = REPLACE(@SqlExc, '{@TableName}', @TableName);
PRINT @SqlExc
-- sp_executesql @SqlExc <-- Do this in production
END