For what it is worth, we have gotten around this by having the called SP return a text string containing the offending SQL code to the calling SP, then executing the code WITHIN the calling SP, rather than calling on the second SP to do the EXEC.
Further, so that the subordinate SP can be called directly for its data output, or called by another SP for further use of the data, we configure it with an output variable as a switch. If we call it with the output variable NULL, it EXECUTES the internal SQL and passes back the data (e.g., to an ASP.NET request). If we need to use it in another SP, we we pass an output variable to it, and get back the SQL text that we then execute inside the other SP.
Here is a simple example which uses a table tblDevices which has fields Device_Id (int) and FirstName (varchar(30)) amongst other things. There is a record with Device_Id = 10275.
Subordinate SP (@r is the "switch")
CREATE PROCEDURE [dbo].[sp_TestNested1] (@Device_ID Int, @r VARCHAR(MAX) out)
DECLARE @sSql VARCHAR(MAX)
SELECT @sSql = 'DECLARE @Table TABLE(FirstName VARCHAR(30))
INSERT INTO @Table(FirstName) (SELECT FirstName FROM tblDevices WHERE Device_Id = ' + CAST(@Device_Id AS VARCHAR(10)) + ') END
SELECT * FROM @Table'
IF @r IS NULL
SELECT @r = @sSql
CREATE PROCEDURE [dbo].[sp_TestNested2] (@Device_ID Int)
Declare @r VARCHAR(Max)
EXEC dbo.sp_TestNested1 10275, @r OUTPUT
So, if we
EXEC dbo.sp_TestNested1 10275,null, it executes the SQL and we get back the results
If we EXEC dbo.sp_TestNested2 10275, it passes the @r switch, gets back the SQL in @r, and executes the dbo.sp_TestNested1 code internally.
At the end of the day, we have a single object in dbo.sp_TestNested1 that can either be executed itself, or used by any number of other SPs that need to do something with its results without duplicating the dbo.sp_TestNested1 code.