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,那就去做吧,不要偷懒!您的目标应该是最适合数据库使用的目标。
我有一个存储过程 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,那就去做吧,不要偷懒!您的目标应该是最适合数据库使用的目标。