Select 将存储过程的结果转换为 table

Select results from stored procedure into a table

我有一个存储过程 usp_region,它有一个 select 语句,结果集有 50 列。该过程被我们应用程序中的多个其他存储过程调用。

大多数存储过程将参数传递给此过程并显示它returns 的结果集。我有一个存储过程 usp_calculatedDisplay,它从该存储过程中获取列并将这些值插入临时 table 并对这些列进行更多计算。

这是 usp_calculatedDisplay.

中的部分代码
Begin Procedure

/* some sql statements */

Declare #tmptable
(
    -- all the 50 columns that are returned from the usp_region procedure
)

Insert Into #tmptable
    exec usp_region @regionId = @id

Select t.*, /* a few calculated columns here */
From #tmptable t

End of procedure

每次我向 usp_region 过程中添加一列时,我还必须确保必须将其添加到该过程中。否则它会破裂。它变得难以维护,因为当列被添加到 usp_region.

时,有人极有可能错过向 usp_calculatedDisplay 过程添加列

为了克服这个问题,我决定这样做:

Select *
Into #tmptable
From OPENROWSET('SQLNCLI', 
                'Server=localhost;Trusted_Connection=yes;', 
                'EXEC [dbo].[usp_region]')

问题是 'Ad Hoc Distributed Queries' 组件已关闭。所以我不能用这种方法来克服这个问题。我想知道是否有任何其他方法可以克服这个问题。我真的很感激任何帮助。谢谢!

Every time I add a column to the usp_region procedure

SQL服务器是一个结构化的数据库,并不是为了解决这种每天都需要改变结构的情况。

如果您 add/remove 列如此频繁,那么您可能没有选择正确的数据库类型,您最好重新设计您的系统。

It has become difficult to maintain it since it is highly possible for someone to miss adding a column to the usp_calculatedDisplay procedure when the column is added to the usp_region.

对此有两个简单的解决方案 (1) 使用 DDL 触发器 - 非常糟糕的想法,但易于实施和工作。 (2) 用我的技巧select from stored procedure

选项 1:使用 DDL 触发器

您可以自动执行整个过程并在每次更改存储过程 usp_region 时更改存储过程 usp_calculatedDisplay

https://docs.microsoft.com/en-us/sql/relational-databases/triggers/ddl-triggers

基本方法是

CREATE OR ALTER TRIGGER NotGoodSolutionTrig ON DATABASE FOR ALTER_PROCEDURE AS BEGIN
    DECLARE @var_xml XML = EVENTDATA();
    IF(
        @var_xml.value('(EVENT_INSTANCE/DatabaseName)[1]', 'sysname') = 'tempdb'
        and 
        @var_xml.value('(EVENT_INSTANCE/SchemaName)[1]', 'sysname') = 'dbo'
        and 
        @var_xml.value('(EVENT_INSTANCE/ObjectName)[1]', 'sysname') = 'usp_region'
        )
        BEGIN
            -- Here you can parse the text of the stored procedure 
            -- and execute ALTER on the first SP
            -- To make it simpler, you can design the procedure usp_region so the columns names will be in specific row or between to comment which will help us to find it
        
            -- The code of the Stored Procedure which you need to parse is in the value of:
            -- @var_xml.value('(EVENT_INSTANCE/TSQLCommand/CommandText)[1]', 'NVARCHAR(MAX)'))
            -- For example we can print it
            DECLARE @SP_Code NVARCHAR(MAX)
            SET @SP_Code = CONVERT(NVARCHAR(MAX), @var_xml.value('(EVENT_INSTANCE/TSQLCommand/CommandText)[1]', 'NVARCHAR(MAX)'))
            PRINT @SP_Code
            
            -- In your case, you need to execute ALTER on the usp_calculatedDisplay procedure using the text from usp_region
        END
END

选项 2:使用 sys.dm_exec_describe_first_result_set

从存储过程中欺骗 select

这是获得所需内容的简单直接的方法。

CREATE OR ALTER PROCEDURE usp_calculatedDisplay AS 

    -- Option: using simple table, so it will exists outsie the scope of the dynamic query
    DROP TABLE IF EXISTS MyTable;
    DECLARE @sqlCommand NVARCHAR(MAX)
    select @sqlCommand = 'CREATE TABLE MyTable(' + STRING_AGG ([name] + ' ' +  system_type_name, ',') + ');'
    from sys.dm_exec_describe_first_result_set (N'EXEC usp_region', null,0)
    PRINT @sqlCommand
    EXECUTE sp_executesql @sqlCommand

    INSERT MyTable EXECUTE usp_region;

    SELECT * FROM MyTable;

GO

注意!!! 不建议在生产中使用这两种解决方案。我的建议是通过重新设计您的系统来避免此类需求。如果您需要重写 20 SP,那就去做吧,不要偷懒!您的目标应该是最适合数据库使用的目标。