Concatenating nvarchar(max) values doesn't seem to work (+= working as =)

坚强是说给别人听的谎言 提交于 2019-12-08 02:43:53

问题


Using SQL Server 2012, I've found that trying to build up a string based on an nvarchar(max) column in a table doesn't seem to work correctly. It seems to overwrite, instead of append. Arbitrary Example:

    DECLARE @sql nvarchar(max);
    SELECT @sql = N'';
    SELECT @sql += [definition] + N'
GO
'
    FROM sys.sql_modules 
    WHERE OBJECT_NAME(object_id) LIKE 'dt%'
    ORDER BY OBJECT_NAME(object_id);

    PRINT @sql;

This SHOULD print out all the SQL module definitions for all the various dt_ tables in the database, separated by GO, as a script that could then be run. However... this prints out only the LAST module definition, not the sum of all of them. It's behaving as if the "+=" were just an "=".

If you change it just slightly... cast [definition] to an nvarchar(4000) for example, it suddenly works as expected. Also, if you choose any other column that is NOT an nvarchar(max) or varchar(max) type, it works as expected. Example:

DECLARE @sql nvarchar(max);
SELECT @sql = N'';
SELECT @sql += CAST([definition] AS nvarchar(4000)) + '
GO
'
FROM sys.sql_modules 
WHERE OBJECT_NAME(object_id) LIKE 'dt%'
ORDER BY OBJECT_NAME(object_id);

PRINT @sql;

Is this a known bug? Or is this working as expected? Am I doing something wrong? Is there any way for me to make this work correctly? I've tried a dozen different things, including ensuring every expression in the concatenation is the same nvarchar(max) type, including the string literal.

NOTE: The example is just an example that shows the problem, and not exactly what I'm trying to do in real life. If your database doesn't have the "dt*" tables defined, you can change the WHERE clause to specify any group of tables or stored procedures in any database you want, you'll get the same result... only the last one shows up in the @sql string, as if you just did "=" instead of "+=". Also, explicitly stating "@sql = @sql + " behaves the same way... works correctly with every string type EXCEPT nvarchar(max) or varchar(max).

I've verified that none of the [definition] values is NULL as well, so there are no NULL shenanigans going on.


回答1:


The += operator only applies to numeric data types in SQL Server. Microsoft documentation here

For string concatenation, you need to write the assignment and concatenation separately.

DECLARE @sql nvarchar(max);
SELECT @sql = N'';
SELECT @sql = @sql + [definition] + N'
GO
'
FROM sys.sql_modules 
WHERE OBJECT_NAME(object_id) LIKE 'dt%'
ORDER BY OBJECT_NAME(object_id);

PRINT @sql;

Also, if you are running this query in Management Studio, keep in mind that there is a limit to the size of the data that it will return (including in a print statement). So if the definitions of your modules exceed this limit, they will be truncated in the output.




回答2:


It works as expected:

DECLARE @sql nvarchar(max);
SELECT @sql =COALESCE(@sql + N' GO ','')+[definition]
FROM sys.sql_modules 


Print @sql;



回答3:


The following code demonstrates taking a column value from a set of ordered rows and, using for xml, creating an aggregated string with separators, including line breaks, between values.

-- Carriage return and linefeed characters.
declare @CRLF as NVarChar(16) = NChar( 13 ) + NChar( 10 );
-- Carriage return and linefeed characters XML encoded.
declare @EncodedCRLF as NVarChar(16) = N'
';
-- Separator to insert between source rows in the accumulated string.
declare @Separator as NVarChar(64) = ';' + @EncodedCRLF + 'go;' + @EncodedCRLF + 'do something with ';
-- Characters in   @Separator   to remove from the first element in   @Result .
declare @SkipCount as Int = 4 + 2 * Len( @EncodedCRLF );
-- The result.
declare @Result as NVarChar( max ) = '';

-- Do it.
select @Result =
  Replace(
    Stuff(
      -- Specify the sorting order of the XML elements in the following   select   statement.
      ( select @Separator + name from sys.tables order by name desc for XML path(''), type).value('.[1]', 'VarChar(max)' ),
      1, @SkipCount, '' ),
    @EncodedCRLF, @CRLF );

-- Output the result.
select @Result as [Result];
-- Output the result with line breaks shown.
print @Result; -- Switch to   Messages   tab in SSMS to see result.



回答4:


To add on to @HABO's comment, the behavior of aggregate string concatenation is undefined and results are plan dependent. Use FOR XML to provide deterministic results and honor the ORDER BY clause:

DECLARE @sql nvarchar(max);

SET @sql = 
(SELECT [definition] + N'
GO
'
FROM sys.sql_modules 
WHERE OBJECT_NAME(object_id) LIKE 'dt%'
ORDER BY OBJECT_NAME(object_id)
FOR XML PATH(''), TYPE).value('(./text())[1]', 'nvarchar(MAX)');

PRINT @sql; --SSMS will truncate long strings



回答5:


I´m pretty sure the PRINT command is the reason for the troubles.

When I try:

DECLARE @SQL NVARCHAR(MAX) = N'';
SELECT @sql += SUBSTRING( [definition], 1, 100 ) + CHAR(13) + CHAR(10) + 'GO' + CHAR(13) + CHAR(10)
FROM SYS.SQL_MODULES 
PRINT @sql;

I get all my procedures (the first 100 chars ...). So it seems to be a limitation to the PRINT command.

Second try, another database, without "substring":

DECLARE @SQL NVARCHAR(MAX) = N'';
SELECT @sql += [definition] + CHAR(13) + CHAR(10) + 'GO' + CHAR(13) + CHAR(10)
FROM SYS.SQL_MODULES 
PRINT @sql;

Now I got 1,5 procedures: The 1st one complete, the second stopped in the middle of on line in the proc. There is a limit in PRINT

Third try, to proof my assumption:

DECLARE @SQL NVARCHAR(MAX) = N'';
SELECT @sql += [definition] + 'GO' FROM SYS.SQL_MODULES 
PRINT @sql;

Now I got one line of code more! So for me, this is the proof, that PRINT has somehow a limit.

A last attempt:

DECLARE @SQL NVARCHAR(MAX) = N'';
SELECT @sql += [definition] + N'GO' FROM SYS.SQL_MODULES 
PRINT SUBSTRING(@SQL,1,4000)
PRINT SUBSTRING(@SQL,4001,4000)

The result: Now I got more then 2 procedures, although there now is a line break after 4K, this may occur on ... bad places.



来源:https://stackoverflow.com/questions/51315886/concatenating-nvarcharmax-values-doesnt-seem-to-work-working-as

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!