Sql-Server

如何在sqlcmd中輸出超過4000個字元

  • May 3, 2018

我製作了一個儲存過程restoredatabase,它返回程式碼以恢復數據庫。輸出腳本如下所示:

restore database [DB]  from disk='F:\FULL1.bak' with norecovery
restore database [DB]  from disk='F:\DIFF1.bak' with norecovery
restore log [DB]  from disk='F:\log1.bak' with norecovery
restore log [DB]  from disk='F:\log2.bak' with norecovery
...
restore log [DB]  from disk='F:\logN.bak' with norecovery
restore database [DB] with recovery

在整個 SP 中,這些行被添加到一個參數@sql varchar(max)中,最後用 列印print(@sql),如果使用者指定了正確的標誌,則用 執行exec(@sql)。兩者都完美地工作。但是,當像這樣使用 sqlcmd 呼叫 SP 時:

set restoresql=exec restoredatabase @DB='Databasename', @Execute='y'
sqlcmd -h-1 -S SRV\T2 -U sa -P sa -d master -Q "%restoresql%" -o output.txt

輸出文件output.txt將始終最多包含 4002 個字元。

我試過添加-y 8000,但這沒有幫助。如何獲取輸出文件以提供完整的生成腳本?

需要明確的是,問題實際上出在PRINT命令而不是SQLCMD實用程序上。

我猜你的變數實際上是NVARCHAR(MAX),而不是VARCHAR(MAX)因為PRINT命令僅限於使用NCHAR/的 4000 個字元NVARCHARNVARCHAR否則,它可以使用/輸出最多 8000 個字元CHAR。要查看VARCHAR超過 4000 個字元但不超過 8000 個字元,請執行以下命令:

sqlcmd -Q "DECLARE @Yo VARCHAR(MAX) = REPLICATE(CONVERT(VARCHAR(MAX), '#'), 9000); PRINT @Yo;" -o out.txt

如果您需要列印超過 8000 個字元,那麼您需要以最多 8000 個字元的塊迭代變數(該PRINT命令一次最多只能顯示 8000 個CHAR/個VARCHAR字元)。但是您不想簡單地將其切成 8000 個字元塊,因為這可能位於不應拆分的內容的中間,例如單詞/對象名稱/編號等。因為該PRINT命令將始終包含換行符最後的字元,最好列印到每個 8000 個字元塊的最後一個換行符。結果是,在多個顯示行中拆分的唯一行是那些在下一個換行符之前超過 8000 個字元的行,對此真的無能為力。

嘗試使用以下 proc 代替您的print(@sql)命令(僅供參考:您不需要像@sqlforPRINT那樣用括號括起來EXEC)。請注意,在字元串中查找最後一個換行符需要創建一個LastIndexOf函式,因為 T-SQL 中沒有內置函式。您可以創建一個 T-SQL UDF 來執行此操作。您也可以使用 SQLCLR,在這種情況下,您可以自己編寫程式碼,或者直接獲取免費版本的SQL#庫(我是該庫的作者)。

SET ANSI_NULLS, QUOTED_IDENTIFIER ON;

IF (OBJECT_ID(N'dbo.Display') IS NOT NULL)
BEGIN
 DROP PROCEDURE dbo.Display;
END;

GO
CREATE PROCEDURE dbo.Display
(
 @TextToDisplay VARCHAR(MAX)
)
AS
SET NOCOUNT ON;

 DECLARE @Length INT = LEN(@TextToDisplay),
         @Buffer VARCHAR(8000),
         @BufferLength INT,
         @StartIndex INT = 1,
         @LastNewlineIndex INT;

 SET @TextToDisplay = REPLACE(@TextToDisplay, CHAR(13), ''); -- normalize

 WHILE (1 = 1)
 BEGIN
   SET @Buffer = SUBSTRING(@TextToDisplay, @StartIndex, 8000);
   SET @BufferLength = DATALENGTH(@Buffer);

   IF (@BufferLength < 8000)
   BEGIN
     BREAK;
   END;

   SET @LastNewlineIndex =
                  SQL#.String_LastIndexOf(@Buffer, CHAR(10), @BufferLength, 1);

   IF (@LastNewlineIndex > 0)
   BEGIN
     PRINT SUBSTRING(@Buffer, 1, (@LastNewlineIndex - 1));
     SET @StartIndex += @LastNewlineIndex;
   END;
   ELSE
   BEGIN
     PRINT @Buffer;
     SET @StartIndex += @BufferLength;
   END;
 END; -- WHILE (1 = 1)

 -- Don't print empty line if final chunk was 8000 chars leaving final loop with 0
 IF (DATALENGTH(@Buffer) > 0)
 BEGIN
   PRINT @Buffer;
 END;
GO

測試:

DECLARE @Test VARCHAR(MAX);
SET @Test = '';
SET @Test = @Test + '1' + REPLICATE('a', 7998) + '1';
SET @Test = @Test + '2' + REPLICATE('b', 7998) + '2';
SET @Test = @Test + '3' + REPLICATE('c', 7998) + '3';

SELECT @Test;

PRINT '=============================';
EXEC dbo.Display @Test;
PRINT '=============================';
----
SET @Test = '';
SET @Test = @Test + '1' + REPLICATE('a', 7998) + '1';
SET @Test = @Test + '2' + REPLICATE('b', 7998) + '2';
SET @Test = @Test + '3' + REPLICATE('c', 7000) + '3';

SELECT @Test;

PRINT '=============================';
EXEC dbo.Display @Test;
PRINT '=============================';
----
SET @Test = '';
SET @Test = @Test + '1.1' + REPLICATE('a', 7000) + '1.1' + CHAR(13) + CHAR(10);
SET @Test = @Test + '1.2' + REPLICATE('a', 400) + '1.2' + CHAR(13) + CHAR(10);
SET @Test = @Test + '1.3' + REPLICATE('a', 4000) + '1.3' + CHAR(13) + CHAR(10);
SET @Test = @Test + '2' + REPLICATE('b', 1798) + '2' + CHAR(13) + CHAR(10);
SET @Test = @Test + '3' + REPLICATE('c', 7000) + '3';

SELECT @Test;

PRINT '=============================';
EXEC dbo.Display @Test;
PRINT '=============================';

筆記:

  1. 可以使用內置CHARINDEX函式來查找每個下一個換行符,在這方面它的功能幾乎就像使用字元串拆分函式一樣。此處需要的內容與在換行符上進行簡單拆分之間的區別在於,返回的任何超過 8000 個字元的元素仍需要拆分為不超過 8000 個字元的塊。
  2. 為了處理NVARCHAR(MAX)而不是VARCHAR(MAX),請使用我發佈到 PasteBin 的程式碼:T-SQL Stored Proc to PRINT NVARCHAR(MAX) values
  3. Util_PrintSQL# 庫中有一個儲存過程 ,它處理NVARCHAR(MAX)並具有一些超出上面顯示的 T-SQL 程式碼的功能,但這僅在完整(即付費)版本中可用,在免費版本中不可用。但是這裡顯示的程式碼將處理大多數情況。

引用自:https://dba.stackexchange.com/questions/97188