串联nvarchar(max)值似乎不起作用(+ =作为=工作)

时间:2018-07-12 23:44:31

标签: sql-server tsql sql-server-2012

使用SQL Server 2012,我发现尝试基于表中的nvarchar(max)列构建字符串似乎无法正常工作。它似乎覆盖,而不是追加。任意示例:

    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;

这应该打印出数据库中所有不同dt_表的所有SQL模块定义,并用GO分隔,然后可以运行该脚本。但是...这只会打印出LAST模块定义,而不是所有模块的总和。表现得好像“ + =”只是一个“ =”。

如果稍稍更改一下...例如,将[definition]强制转换为nvarchar(4000),它会突然按预期工作。另外,如果您选择不是nvarchar(max)或varchar(max)类型的任何其他列,则它会按预期工作。示例:

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;

这是一个已知的错误吗?还是按预期工作?难道我做错了什么?我有什么办法可以使其正常工作?我尝试了十多种不同的方法,包括确保串联中的每个表达式都是相同的nvarchar(max)类型,包括字符串文字。

注意:该示例只是一个显示问题的示例,并不完全是我在现实生活中想要做的事情。如果您的数据库没有定义“ dt *”表,则可以更改WHERE子句以指定所需的任何数据库中的表组或存储过程组,您将得到相同的结果……仅是最后一个出现在@sql字符串中,就像您只是使用“ =”而不是“ + =”一样。另外,显式声明“ @sql = @sql +”的行为方式相同...除了nvarchar(max)或varchar(max)以外的每种字符串类型均可正常使用。

我已经证实[definition]值也都不为NULL,因此没有NULL的恶作剧。

5 个答案:

答案 0 :(得分:1)

+ =运算符仅适用于SQL Server中的数字数据类型。 Microsoft documentation here

对于字符串串联,您需要分别编写赋值和串联。

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;

此外,如果您正在Management Studio中运行此查询,请记住,它将返回的数据大小(包括在print语句中)受到限制。因此,如果模块的定义超出此限制,则它们将在输出中被截断。

答案 1 :(得分:0)

它按预期工作:

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


Print @sql;

答案 2 :(得分:0)

我很确定PRINT命令是造成麻烦的原因。

当我尝试:

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;

我得到了所有程序(前100个字符...)。因此,这似乎是PRINT命令的限制。

第二次尝试,另一个没有“子字符串”的数据库:

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

现在我得到了1,5个程序:第一个完成,第二个停止在proc的联机中间。 PRINT中有一个限制

第三次尝试,以证明我的假设:

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

现在我又获得了一行代码!所以对我来说,这就是PRINT在某种程度上有局限性的证明。

最后一次尝试:

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

结果:现在我得到了2个以上的过程,尽管4K之后出现了换行符,但是这可能发生在...不好的地方。

答案 3 :(得分:0)

以下代码演示了从一组有序行中获取列值,并使用def makePDF(path, filename, image_files): output_file = path + '/' + filename + ".pdf" try: with open(output_file,"wb") as f: f.write(img2pdf.convert(image_files)) #f.close() except IOError as error: print ('opss') pass 创建了一个聚合字符串,这些字符串之间有分隔符,包括换行符。

for xml

答案 4 :(得分:0)

要添加到@HABO的注释中,未定义聚合字符串串联的行为,并且结果取决于计划。使用FOR XML提供确定性结果并遵守ORDER BY子句:

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