Sql-Server

查詢中的大字元串連接

  • April 26, 2019

我正在嘗試在 T-SQL 中連接字元串,例如

'This is a test ' + CHAR(13) + CHAR(10) + ' string'

當字元串很長時(我不知道限制),我得到一個錯誤:

您的 SQL 語句的某些部分嵌套得太深。重寫查詢或將其分解為更小的查詢。

我該如何解決這個問題?SQL 語句是如何“嵌套”的?

我應該切換到 0x 嗎????如果字元串長度超過某個值,則表示法?

這個問題在 10 年前被問到

https://social.msdn.microsoft.com/Forums/sqlserver/en-US/e7244cf6-049e-4b8a-b230-226fdd96a694/large-string-concatenation?forum=transactsql

沒有得到滿意的答复。

編輯

  • 要插入的總字元串(帶有連接和 ‘:s)是 369711 個字元長。
  • 有 6301 個char(13)和相同的實例char(10)
  • 有 19050 個實例+,不一定都用作連接運算符。

這可能取決於“太長”的含義:

  • **長度:**如果您的意思是許多字元的字元串,那麼嘗試將其中一些轉換為一種MAX類型(即NVARCHAR(MAX)VARCHAR(MAX))。例如:
CONVERT(VARCHAR(MAX), 'This is a test ') + CHAR(13) + CHAR(10) + ' string'
  • **連接:**如果您的意思是涉及許多連接的操作,那麼在單個語句中肯定有一個最大數量可以完成。我前段時間對此進行了測試,並打算在部落格上討論它,但分心了;-)。我剛剛回顧了我的測試,雖然有幾種不同的連接方法,但最簡單的方法(這就是你正在做的)每個語句的限制約為 3300。我說“大約” 3300 是因為它會根據與記憶體相關的其他一些因素而有所不同。有時我可以得到 3310,然後我嘗試了 3311,它可以工作,再試一次,它失敗,用 3310 再試一次,它失敗,再用 3309 再試一次,它可以工作。

如果你有超過這個數量,那麼你別無選擇,只能將操作分成多個步驟(即多個SET/SELECT語句)。雖然,這里達到最大值的錯誤是:

消息 8631,級別 17,狀態 1,行 XXXX

內部錯誤:已達到伺服器堆棧限制。請在您的查詢中尋找潛在的深層嵌套,並嘗試簡化它。

而且您並沒有完全做到這一點,因此在您進行更多測試或至少提供錯誤號之前還不能完全確定。在任何一種情況下,您都可以通過將“\r”和“\n”組合成單個變數或VARBINARY文字來減少串聯的數量。以下範例顯示兩種方法產生相同的輸出:

PRINT 'This is a test ' + CHAR(13) + CHAR(10) + ' string';
PRINT '-----------------------------';
PRINT 'This is a test ' + CONVERT(CHAR(2), 0x0D0A) + ' string';

PRINT '-----------------------------';
DECLARE @CRLF CHAR(2) = CHAR(13) + CHAR(10);
PRINT 'This is a test ' + @CRLF + ' string';

該變數的好處是它更短,這有助於使大型腳本更具可讀性而不是那麼大。但是,您不能突出顯示一條或多條隨機行並執行,除非您還突出顯示DECLARE但可能並不總是有效。文字的好處VARBINARY是您可以輕鬆執行隨機分組的語句,或將語句移動到該腳本的其他腳本/部分,而不必擔心忘記DECLARE. 在任何一種情況下,這可能會減少數百個串聯(假設您已經有大約 3300 個串聯),但如果您獲得需要數百個串聯的字元串值,它不會阻止錯誤。

也很可能有一種不同和/或更簡單的方法來完成您最終嘗試完成的工作,而不依賴於串聯。因此,如果您可以更新問題以指定您首先這樣做的原因,這將有所幫助:-)。

有 6301 個char(13)和相同的實例char(10)

給定每組“CR”+“LF”的三個串聯(即+ CHAR(13)一個、+ CHAR(10)兩個和+ '...'第三個),即有 18,903 個串聯。是的,這有點超過 3300 ;-)。

我希望這些insert into陳述是單行的。

行。但為什麼?你實際上從中獲得了什麼?嵌入式 CRLF 非常適合插入。

還:

  1. 這些數據的來源是什麼?它來自 SQL Server 嗎?
  2. 是字元串VARCHAR還是NVARCHAR?在問題的範例中,它只是VARCHAR,但在對問題的評論中,您給出了將VARBINARY文字轉換為 的範例NVARCHAR

在任何一種情況下,執行我上面提到的兩種方法中的任何一種來減少每個 CRLF 的兩個部分之一的連接都無濟於事,因為這仍然會給您留下 12,602 個連接。所以,你有兩個選擇:

  1. 使用替換/轉義序列在對該問題的評論中,Martin Smith 建議使用{crlf}. 我的偏好是以下之一:

  2. CHAR(31):這是不可列印的“單位分隔符”字元。它只是一個字節,不太可能出現在您的字元串數據中,如果您不小心更改了編碼,也不會轉換為其他內容。不幸的是,它的可讀性不是很好,因為它甚至不會顯示為空格,因此它分隔的單詞之間似乎沒有任何內容。

PRINT REPLACE('one' + CHAR(31) + 'two', CHAR(31), CHAR(13)+CHAR(10));
 `'\n'`:這是幾乎通用的“換行符”轉義序列。這是兩個字節,絕對更具可讀性。
INT REPLACE('one\ntwo', '\n', CHAR(13)+CHAR(10));

`無論哪種情況,您只需`INSERT INTO Schema.Table (Column) VALUES (REPLACE(....));`
2. 將整個字元串轉換為`VARBINARY`: 這會產生一個非常便攜的單行值。這是不可讀的,但也不會有任何意外的字元轉換,您可以使用標準的 ANSI/程式碼頁 1252 文件編碼,而您需要使用 UTF-8(帶簽名/BOM)或 Unicode / UTF-16(帶簽名 / BOM)用於文件編碼,如果字元串數據是 Unicode / `NVARCHAR`.


您在對該問題的評論中提到了有關擁有字節數組的內容。如果數據的來源是 .NET,那麼字元串數據已經是 UTF-16 LE(即`NVARCHAR`),所以只要做`INSERT INTO Schema.Table (Column) VALUES (0x{hex_from_byte[]});`就可以了。如果目標是一`VARCHAR`列,那麼您可以:


 使用以下內容在 .NET 中進行轉換:`Encoding.GetEncoding(1252).GetBytes(_StringVariable)`
 使用 T-SQL 轉換:`INSERT INTO Schema.Table (Column) VALUES (CONVERT(NVARCHAR(MAX), 0x{hex_from_byte[]}));`. 請注意,我們正在轉換為,`NVARCHAR`因為十六進製表示是 UTF-16 LE。函式的返回值將在插入列時`CONVERT`隱式轉換為。`VARCHAR`

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