好的..我已经放弃并回到我的老朋友 xpcmdshell。在整个响应及其代码中,xpcmdshell 将隐含下划线 (_),因为我经常无法加载包含全名的页面。
首先,这只是我尝试过但不起作用的三件事(我不记得所有其他的):
- 设置无计数
- 适用于没有临时表的任何 SP,但由于大多数 2500+- 我将研究如何使用它们,这是不可行的。
- 无操作
- 我创建了一个动态创建 No Op 的过程,但是在实现时我无法找到解决 SQL 陷入嵌套循环的方法。
- bcp 查询输出
因此,在多次抨击和谷歌搜索之后,我又回到了 xpcmdshell。下面的脚本(我将把它变成一个过程)接受一个 SP exec 语句和在其下运行它的数据库,将一个 xpcmdshell sqlquery 命令格式化为一个文件,执行该文件并将它的输出插入到一个临时表中,然后将这些结果的列标题提取到另一个临时表中。
SET NOCOUNT ON
DECLARE @TempCmdPath VARCHAR(MAX),
@ProcedureExec VARCHAR(MAX),
@DatabaseName VARCHAR(255)
SELECT @TempCmdPath = 'C:\Temp\' --Make sure path ends with a '\' (or add logic to append if missing)
SELECT @ProcedureExec = 'exec dbo.crp_rpt_GetCustomerDetails @ShowContacts=0,@CustomerName=''cust123%''' --Make sure to double up the single quotes (')
SELECT @ProcedureExec = REPLACE(@ProcedureExec, '''', '''''') --Double the single quotes again (') for use in xpcmdshell sqlquery command
SELECT @DatabaseName = 'CorpDB'
IF OBJECT_ID('tempdb.dbo.#CmdOut','U') IS NOT NULL
DROP TABLE dbo.#CmdOut
CREATE TABLE dbo.#CmdOut
(
id INT IDENTITY(1,1), --Used in ROW_NUMBER() function to update rid
rid INT, --Actual number for use in WHILE loop
LineOut VARCHAR(MAX)
)
DECLARE @cmdshell VARCHAR(MAX)
/* Create a file with the commands to run */
SELECT @cmdshell = 'exec master.dbo.xpcmdshell ''sqlcmd '
+ REPLACE( '-q "PRINT '':error ' + @TempCmdPath + 'TempSqlCmdOut.txt'' ' --Set errors to be directed to a text file
+ 'PRINT ''' + @ProcedureExec + '''" ' --Add additional PRINT statements to include more statements to run
+ '-o "' + @TempCmdPath + 'TempSqlCmd.txt" ' --Specify where the file should output to
, '''', '''''' ) --Double up the single quotes (') /again/ for this statement
+ '''' --Close the statement
PRINT @cmdshell
INSERT INTO dbo.#CmdOut ( LineOut )
EXEC ( @cmdshell )
/* Execute the commands stored in the file we just created */
SELECT @cmdshell = 'exec master.dbo.xpcmdshell ''sqlcmd '
+ '-d ' + @DatabaseName + ' '
+ '-r 1 ' --Set any additional messsages to be treated as errors. This, combined with the ":error <path>\TempSqlCmdOut.txt" line above, will ensure that print statements are not returned in the output
+ '-i "' + @TempCmdPath + 'TempSqlCmd.txt" '
+ '-s "," ' --Column Separator
+ '''' --Close the statement
PRINT @cmdshell
INSERT INTO dbo.#CmdOut ( LineOut )
EXEC ( @cmdshell )
/* Clean up. Delete the two temp files */
SELECT @cmdshell = 'exec master.dbo.xpcmdshell ''del "' + @TempCmdPath + 'TempSqlCmd.txt"'''
PRINT @cmdshell
INSERT INTO dbo.#CmdOut ( LineOut )
EXEC ( @cmdshell )
SELECT @cmdshell = 'exec master.dbo.xpcmdshell ''del "' + @TempCmdPath + 'TempSqlCmdOut.txt"'''
PRINT @cmdshell
INSERT INTO dbo.#CmdOut ( LineOut )
EXEC ( @cmdshell )
/* Clean up NULL rows then update the rid column's value */
DELETE dbo.#CmdOut
WHERE LineOut IS NULL
UPDATE co
SET rid = n.rid
FROM dbo.#CmdOut co
INNER JOIN ( SELECT id,
ROW_NUMBER() OVER ( ORDER BY id ) AS [rid]
FROM dbo.#CmdOut
) AS n ON co.id = n.id
--SELECT * FROM dbo.#CmdOut
---------------------------------------------------------------
---------------------------------------------------------------
IF OBJECT_ID('tempdb.dbo.#SPResultHeaders','U') IS NOT NULL
DROP TABLE dbo.#SPResultHeaders
CREATE TABLE dbo.#SPResultHeaders
(
id INT IDENTITY(1,1),
HeaderName VARCHAR(500)
)
DECLARE @LineCount INT,
@LineIndex INT,
@Delimiter VARCHAR(10),
@PrevDelimitCharIndex INT,
@NextDelimitCharIndex INT,
@LineText VARCHAR(MAX),
@EndOfLineText VARCHAR(MAX),
@FoundDivider BIT
SELECT @Delimiter = ',',
@FoundDivider = 0
SELECT @LineCount = COUNT(*),
@LineIndex = 1
FROM dbo.#CmdOut
/* Until we move through all of the output lines OR we run into the line between the headers and their data (divider).. */
WHILE ( @LineIndex <= @LineCount
AND @FoundDivider = 0
)
BEGIN
/* Reset DelimitCharIndex: */
SELECT @PrevDelimitCharIndex = 0,
@NextDelimitCharIndex = 1
/* Until the Delimiter is not found.. */
WHILE ( @NextDelimitCharIndex <> 0
AND @FoundDivider = 0
)
BEGIN
/* Search for the Delimiter starting after the last one's position */
SELECT @NextDelimitCharIndex = CHARINDEX(@Delimiter, LineOut, @PrevDelimitCharIndex)
FROM dbo.#CmdOut
WHERE rid = @LineIndex
/* If another Delimiter is found on this line.. */
IF ( @NextDelimitCharIndex <> 0 OR @EndOfLineText IS NOT NULL )
BEGIN
/* Make sure we're don't have left overs from a previous line */
IF ( @EndOfLineText IS NOT NULL )
BEGIN
/* If we do, set the current string to the previous + the current */
SELECT @LineText = @EndOfLineText + SUBSTRING(LineOut, @PrevDelimitCharIndex, (@NextDelimitCharIndex - @PrevDelimitCharIndex))
FROM dbo.#CmdOut
WHERE rid = @LineIndex
/* Then clear out the left overs */
SELECT @EndOfLineText = NULL
END
ELSE
BEGIN
/* Get the text between the previous delimiter and the next */
SELECT @LineText = SUBSTRING(LineOut, @PrevDelimitCharIndex, (@NextDelimitCharIndex - @PrevDelimitCharIndex))
FROM dbo.#CmdOut
WHERE rid = @LineIndex
END
/* After the column headers in the output it will have a divider consisting of hyphens (-) (split by whatever we specified for the -s argument of the sqlcmd)
Check to see if our text is purely hyphens. IF NOT, insert the text into our result table and increment Header Count by 1. IF SO, set the FoundDivider flag to 1.
*/
IF ( LTRIM(RTRIM(REPLACE(@LineText, '-', ''))) <> '' )
BEGIN
IF ( CHARINDEX('-', @LineText) <> 0 )
BEGIN
/* If there are more than three hyphens in a row, assume it's the divider and set @FoundDivider to 1 to exit while */
IF ( SUBSTRING(@LineText, CHARINDEX('-', @LineText), 3) = '---' )
SELECT @FoundDivider = 1
ELSE
INSERT INTO dbo.#SPResultHeaders ( HeaderName )
SELECT LTRIM(RTRIM(@LineText))
END
ELSE
BEGIN
INSERT INTO dbo.#SPResultHeaders ( HeaderName )
SELECT LTRIM(RTRIM(@LineText))
END
END
ELSE
BEGIN
/* If there are more than three hyphens in a row, assume it's the divider and set @FoundDivider to 1 to exit while */
IF ( SUBSTRING(@LineText, CHARINDEX('-', @LineText), 3) = '---' )
SELECT @FoundDivider = 1
END
END
/* If another Delimiter is NOT found on this line.. */
ELSE
BEGIN
/* Move remainder of this line's text to @EndOfLineText ("left overs") for use in next itteration */
SELECT @LineText = NULL,
@EndOfLineText = SUBSTRING(LineOut, @PrevDelimitCharIndex, (LEN(LineOut) + 1))
FROM dbo.#CmdOut
WHERE rid = @LineIndex
END
/* Update previous Delimiter's position */
SELECT @PrevDelimitCharIndex = @NextDelimitCharIndex + 1
END --WHILE ( @NextDelimitCharIndex <> 0 )
SELECT @LineIndex = @LineIndex + 1
END --WHILE ( @LineIndex <= @LineCount )
SELECT *
FROM dbo.#SPResultHeaders
如果您打算使用此代码,请不要忘记将 xpcmdshell 替换为 xp(_)cmdshell
希望这对某人有帮助!请不要犹豫,发表您可能有的任何问题、意见或建议。