长话短说,我有一个第三方应用程序,当它无法检索查询/存储过程的元数据时,其行为会有所不同。
众所周知,sys.sp_describe_first_result_set当使用临时表时,它无法检索存储过程的元数据。
sys.sp_describe_first_result_set
为了简单起见,这是一个简单的示例。
CREATE PROCEDURE dbo.Test @Seed INT = 0 AS BEGIN CREATE TABLE #MyTemp ( ID INT NOT NULL ); INSERT INTO #MyTemp (ID) VALUES (@Seed + 1) , (@Seed + 2) , (@Seed + 3) ; SELECT ID FROM #MyTemp END
执行此SP将返回一个结果集,其中有一个列(ID)和三个记录。
ID
EXEC dbo.Test @Seed = 1;
结果是:
ID ----------- 2 3 4
但是,尝试获取元数据将失败:
EXEC sys.sp_describe_first_result_set @tsql = N'EXEC dbo.Test @Seed = 1;';
Msg 11526, Level 16, State 1, Procedure sp_describe_first_result_set, Line 1 [Batch Start Line 24] The metadata could not be determined because statement 'INSERT INTO #MyTemp (ID) VALUES (@Seed + 1) , (@Seed + 2) , (@Seed + 3)' in procedure 'Test' uses a temp table.
(这是预料之中的,因为这是的已知限制sp_describe_first_result_set)
sp_describe_first_result_set
问题是,当我们的第三方应用程序遇到此错误时,它将执行两次SP(首先分析结果集并创建一个临时表,然后执行一次INSERT .. EXEC将数据加载到它创建的临时表中)。
INSERT .. EXEC
当元数据可用时,它将使用获取元数据,sys.sp_describe_first_result_set并使用该信息创建临时表。
由于我们在2012版以下没有任何SQL Server,因此可以使用该WITH RESULT SETS子句,但是在上述应用中无法对其进行配置或手动提供元数据信息。
WITH RESULT SETS
如何使元数据可用于使用此SP的应用程序?
我在回答中提供了两种解决方案,但我想知道是否有我不知道的一种解决方案。
我当前的解决方案是创建一个包装器存储过程,该过程执行现有的包装器过程,并传递所有参数,但定义结果集的元数据。
继续问题中的示例:
EXEC sp_rename 'dbo.Test', 'Test_Logic', 'OBJECT'; GO CREATE PROCEDURE dbo.Test @Seed INT = 0 AS BEGIN EXEC dbo.Test_Logic @Seed = @Seed WITH RESULT SETS ( ( ID INT ) ) ; END
现在,如果我尝试获取元数据,则可以获取它:
is_hidden column_ordinal name is_nullable system_type_id system_type_name max_length precision scale collation_name user_type_id user_type_database user_type_schema user_type_name assembly_qualified_type_name xml_collection_id xml_collection_database xml_collection_schema xml_collection_name is_xml_document is_case_sensitive is_fixed_length_clr_type source_server source_database source_schema source_table source_column is_identity_column is_part_of_unique_key is_updateable is_computed_column is_sparse_column_set ordinal_in_order_by_list order_by_is_descending order_by_list_length tds_type_id tds_length tds_collation_id tds_collation_sort_id --------- -------------- -------------------------------------------------------------------------------------------------------------------------------- ----------- -------------- -------------------------------------------------------------------------------------------------------------------------------- ---------- --------- ----- -------------------------------------------------------------------------------------------------------------------------------- ------------ -------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------- -------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------- --------------- ----------------- ------------------------ -------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------- ------------------ --------------------- ------------- ------------------ -------------------- ------------------------ ---------------------- -------------------- ----------- ----------- ---------------- --------------------- 0 1 ID 1 56 int 4 10 0 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL 0 0 0 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL 38 4 NULL NULL
(在这里看起来很糟糕,但是有效)
缺点是,现在我必须维护两个SP,并且需要执行后续的SP。
替代解决方案
一种替代解决方案是使用动态sql并sys.sp_executesql与该WITH RESULT SETS子句一起运行它。
sys.sp_executesql
这样做的主要缺点是,SQL Server和VisualStudio(数据库项目)无法跟踪动态SQL中的依赖关系。
看起来像这样:
ALTER PROCEDURE dbo.Test @Seed INT = 0 AS BEGIN CREATE TABLE #MyTemp ( ID INT NOT NULL ); INSERT INTO #MyTemp (ID) VALUES (@Seed + 1) , (@Seed + 2) , (@Seed + 3) ; DECLARE @STMT NVARCHAR(MAX) = N' SELECT ID FROM #MyTemp ;'; EXEC sys.sp_executesql @stmt = @STMT WITH RESULT SETS ( ( ID INT ) ) END
当然,我认为SP越复杂,就越难维护动态SQL,因此我更喜欢第一个解决方案。