简体   繁体   中英

Variable concatenation in T-SQL unexpected result

This is minimal example of code used in a context of generating dynamic SQL, hence the replaces. 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. 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):

在此处输入图片说明

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):

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". I hope your real Stored Procedure(s) don't have the sp_ prefix. If they do, I recommend renaming them; using sp_ comes at a cost. Is the sp_ prefix still a no-no?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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