sql-server - 使用 += 创建动态 SQL 在某些情况下会截断之前的内容

标签 sql-server sql-server-2012 concatenation

在某些情况下,当我尝试使用自连接生成字符串时,我在 SQL Server 2012(最新更新)上遇到了奇怪的行为

@Str += ... 

@Str = @Str + ...

它会截断查询中变量的先前内容,这是连接 NULL 值时的预期行为,除非我不...

这里是代码的简化版本,已减少到最少,以在我的实例上重现该错误。很难重现,因为只需将函数结果复制到临时表(在我的情况下这是不可能的)即可修复它,因此我怀疑查询计划或优化方面的问题。

DECLARE @CTESQL VARCHAR(MAX)= '';

SELECT 
    --TOP 4096--Workaround for SQL SERVER bug dropping previous text in some cases (4096 = max statement in a select clause)
    @CTESQL+= CASE WHEN 1 = ROW_NUMBER() OVER (ORDER BY PvtColumnName) THEN '1'
              ELSE CASE WHEN LAG(PvtColumnName) OVER (ORDER BY PvtColumnName) <> ISNULL(PvtColumnName,
                                                              ColumnName)
                        THEN '2'
                   ELSE '3'
                   END
              END + CASE WHEN PvtColumnName IS NULL THEN '4'
                    ELSE (CASE WHEN 1 = ROW_NUMBER() OVER (ORDER BY t.PvtColumnName DESC)
                               THEN '5'
                          ELSE '6'
                          END)
                    END
FROM
    dbo.ImportDefinition('stgPopulation') t
ORDER BY
    PvtColumnName
  , ColumnId

PRINT (@CTESQL);

表函数“ImportDefinition”返回以下数据:

PvtColumnName       ColumnId    ColumnName
------------------- ----------- --------------------
NULL                3           Country
NULL                2           GMPSubRegion
NULL                4           ISO_Ctry
NULL                9           Source
AgeGroupCode        6           Total
AreaTypeCode        6           Total
AgeGroupCode        7           Under5
AreaTypeCode        7           Under5
AgeGroupCode        8           Urban
AreaTypeCode        8           Urban
NULL                1           RegionFullName
NULL                5           Year

预期结果是:

343414343434363636363625

SQL Server 的实际结果是:

25

一个简单的解决方法是使用“TOP n”来修复它,但我不知道为什么,而且它非常脏。

我曾希望强制 MAXDOP 1 会有所帮助,但运气不佳。

这是我第二次遇到这个问题,因此尽管有多种解决方法,但我真的很想了解正在发生的情况或某处是否存在错误。

感谢您的专业知识。

编辑 这是一个允许重现相同行为的脚本:

IF OBJECT_ID('dbo.MyTable', 'U') IS NOT NULL
    DROP TABLE dbo.MyTable;
CREATE TABLE dbo.MyTable
    (
     F1 VARCHAR(255) NOT NULL
   , F2 NVARCHAR(4000) NULL
    )
ON  [PRIMARY];

GO
INSERT  INTO dbo.MyTable
        (F1, F2)
VALUES
        ('foo', 'a')
,       ('faa', 'b')
,       ('fuu', 'a');


DECLARE @CTESQL VARCHAR(MAX)= '';

SELECT
    @CTESQL+= CASE WHEN 1 = ROW_NUMBER() OVER (ORDER BY F2)
                   THEN '1'
                   ELSE CASE WHEN LAG(F2) OVER (ORDER BY F2) <> ISNULL(F2,
                                                              F1)
                             THEN '2'
                             ELSE '3'
                        END
              END + CASE WHEN F2 IS NULL THEN '4'
                         ELSE (CASE WHEN 1 = ROW_NUMBER() OVER (ORDER BY F2 DESC)
                                    THEN '5'
                                    ELSE '6'
                               END)
                    END
FROM
    MyTable
ORDER BY
    F2;

PRINT (@CTESQL);

最佳答案

抱歉,刚刚重新阅读并注意到将函数的结果复制到表中可以防止问题重现。

除非您想为基表和表值函数创建脚本以允许其他人重现问题,否则任何人能做的最好的事情就是猜测。

我的第一个猜测是您的函数没有返回您认为的结果,但如果是,那么 TVF 和您用来构建的未记录技术之间存在相互作用查询结果中的字符串。

我突出显示未记录是为了提醒您,这正是您正在使用的技术,并且不可能说未记录的行为存在“错误”。 SQL 从来没有打算按照您使用它的方式工作,而且它只是碰巧在大多数情况下以这种方式工作,但不能保证它会一直以这种方式工作,或者在未来的版本中完全以这种方式工作。即使使用 TOP n 修复它也没有记录,并且可能无法在 SQL Server 的 future 版本中工作。

更好的解决方案是开始使用 STUFF() 来进行字符串连接。本网站和互联网上的其他地方已经有大量关于如何执行此操作的示例。

对于“为什么这不起作用?”这个问题,我怀疑您得到的最佳答案是:“这是未记录的行为。谁知道呢?”

编辑回应评论:

  1. 我指的未记录的技术是使用 += 构建字符串变量。请参阅this article ,然后向下滚动到名为“不可靠的方法”的部分。您使用的方法是列出的第二种方法,“在 SELECT 中使用变量串联的标量 UDF”,尽管您没有在 UDF 中使用它。尽管如此,SELECT @var = @var + SomeData... 的技术是未记录的部分,因此不可靠。

  2. 我提到的“使用 STUFF() 的解决方案”与 SqlZim 在他的回答中提出的解决方案相同。同一解决方案同时使用 STUFF()FOR XML。作为简写,我将其称为使用 STUFF(),因为我知道搜索该关键字会找到该解决方案。

关于sql-server - 使用 += 创建动态 SQL 在某些情况下会截断之前的内容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38248625/

相关文章:

c# - 如何将.Net Core应用程序连接到Azure SQL数据库

sql-server - 部署 SSIS 包 - 'Failed to load Assembly Microsoft.SqlServer.Management.IntegrationServicesEnum'

SQL 查询基于当前日期的另一个日期时间列返回唯一日期?

mysql - CONCAT(meta_key,meta_value) 到 wp_postmeta 表中的不同列

php - concat条件在php pdo中不起作用但在mySQL中运行

java - 为什么我使用 .concat() 和 += 使用 Java 字符串得到不同的输出?

sql - 有没有办法用 t-sql 创建 "pivot group"列?

php - 当一个表可以有更多结果时的 SQL 查询

SQL根据case语句部分更新列?

sql - TSQL 存储函数