简体   繁体   English

T-SQL 意外结果中的变量连接

[英]Variable concatenation in T-SQL unexpected result

This is minimal example of code used in a context of generating dynamic SQL, hence the replaces.这是在生成动态 SQL 的上下文中使用的代码的最小示例,因此替换。 The expected result is that all the data is represented in the result and in one text variable (a concatenation of several rows).预期的结果是所有数据都在结果和一个文本变量中表示(多行的串联)。

The obvious solution to this example is to use the 'hacks' we found, but this code is used in existing code multiple places (in production code) so it is NOT a feasible solution to solve by "fixing" code, otherwise we would have the solution, see hacks / fixes.此示例的明显解决方案是使用我们发现的“hacks”,但是此代码在现有代码的多个位置(在生产代码中)使用,因此通过“修复”代码解决不是一个可行的解决方案,否则我们将有解决方案,请参阅黑客/修复。 The hope is that there is a server wide setting that helps this issue or others that have had similar issues with their results and findings.希望有一个服务器范围的设置可以帮助这个问题或其他在结果和发现方面有类似问题的人。

DROP TABLE IF EXISTS #Table1,#Table2,#Table3
GO
SELECT ManagerID,EmployeeID
INTO #Table1
FROM (VALUES(1,1),(1,2)) A(ManagerID,EmployeeID)

SELECT ManagerID,[Name]
INTO #Table2
FROM (VALUES(1,'EvilDoer' )) A(ManagerID,[Name])

SELECT EmployeeID,[Name]
INTO #Table3
FROM (VALUES(1,'SlaveA' ),(2,'SlaveB' )) A(EmployeeID,[Name])

/*
Tested on SQL Server 14.0.3257.3 on both compatibility level 110 and 140;
The issue here is, likely?; the execution plan. 
If you have the two replaces then the plan is "Incorrect", i.e. ComputeScalar is before sort => 1 row (Last)
If you remove the one or two of the replaces then the plan is "Correct", i.e. ComputeScalar is after sort => All rows
Alternative 1, usage of TOP(X) yields a "Correct" result
Alternative 2, Removal of the "ORDER BY"-clause yields a "Correct" result
*/
DECLARE @SQL varchar(max) = ''
SELECT --TOP 1000000
@SQL = @SQL + '
EXECUTE sp_MyProc 
     @Manager = '''+REPLACE(T2.[Name],CHAR(39),CHAR(39) + CHAR(39))+'''
    ,@Worker = '''+REPLACE(T3.[Name],CHAR(39),CHAR(39) + CHAR(39))+'''

'
FROM #Table1 T1
INNER JOIN #Table2 T2
    ON T1.ManagerID = T2.ManagerID
INNER JOIN #Table3 T3
    ON T1.EmployeeID = T3.EmployeeID
ORDER BY  T2.[Name], T3.[Name] 

SELECT @SQL /* EXECUTE (@SQL)*/

Incorrect result (missing data):结果不正确(缺少数据):

EXECUTE sp_MyProc 
     @Manager = 'EvilDoer'
    ,@Worker = 'SlaveB'

Execution plan with incorrect result:结果不正确的执行计划:

在此处输入图片说明

Expected result (all data):预期结果(所有数据):

EXECUTE sp_MyProc 
     @Manager = 'EvilDoer'
    ,@Worker = 'SlaveA'


EXECUTE sp_MyProc 
     @Manager = 'EvilDoer'
    ,@Worker = 'SlaveB'

Execution plan with TOP(N): TOP(N) 的执行计划:

在此处输入图片说明

I would personally suggest using FOR XML PATH ( STRING_AGG if you're on 2017+) and properly quoting your injected values here (I assume [Name] cannot be longer than 128 characters):我个人建议使用FOR XML PATHSTRING_AGG如果你在 2017+ 年)并在此处正确引用你注入的值(我假设[Name]不能超过 128 个字符):

DECLARE @SQL nvarchar(MAX),
        @CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET @SQL = STUFF((SELECT @CRLF + N'EXECUTE sp_MyProc @Manager = ' + QUOTENAME(T2.[Name], '''') + N', @Worker = ' + QUOTENAME(T3.[Name], '''') + N';'
                  FROM #Table1 T1
                       INNER JOIN #Table2 T2 ON T1.ManagerID = T2.ManagerID
                       INNER JOIN #Table3 T3 ON T1.EmployeeID = T3.EmployeeID
                  ORDER BY T2.[Name],
                           T3.[Name]
                 FOR XML PATH(N''), TYPE).value('.', 'nvarchar(MAX)'),1,2,N'');

--PRINT @SQL;
EXEC sys.sp_executesql @SQL;

Note, as well, that the sp_ prefix is reserved by Microsoft to mean "Special Procedure". sp_请注意,Microsoft保留sp_前缀以表示“特殊程序”。 I hope your real Stored Procedure(s) don't have the sp_ prefix.我希望你真正的存储过程没有sp_前缀。 If they do, I recommend renaming them;如果是这样,我建议重命名它们; using sp_ comes at a cost.使用sp_是有代价的。 Is the sp_ prefix still a no-no? sp_ 前缀仍然是禁忌吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM