With Result Set

by Alexey Knyazev 10. августа 2011 16:37

Ещё одна приятная фишка, которая появится в новой версии SQL Server ( aka Denali ) - это параметр WITH RESULT SETS инструкции EXECUTE. Этот параметр позволяет не только делать выборку из процедуры, но и изменять результирующий рекордсет.

На форуме SQL.RU достаточно часто возникает вопрос, как сделать "Select из результата выполнения хранимой процедуры". В местном FAQ есть даже отделная ссылка с вариантами решения этой задачи - http://www.sql.ru/faq/faq_topic.aspx?fid=416.

Прежде, чем показать, как на лету менять рекордсет из процедуры, а именно типы полей и их имена в SQL Server 2011, вспомним, как решить эту задачу в текущих версиях SQL Server.
Для начала создадим тестовую таблицу из которой и будем делать выборку в теле процедуры:

if object_id ( 'dbo.Table01', 'U' ) is not null
drop table dbo.Table01
go
create table dbo.Table01 ( id int identity primary key
                         , val varchar(50)
                         , dt datetime
                         )
go
insert into dbo.Table01
select 'Val01', '20110801'
union all
select 'Val02', '20110802'
union all
select 'Val03', '20110803'
union all
select 'Val04', '20110804'
union all
select 'Val05', '20110805'
go

select * from dbo.Table01



А теперь создадим процедуру, которая делает выборку из этой таблицы:
if object_id ( 'dbo.Proc01', 'P' ) is not null
drop procedure dbo.Proc01
go
--Процедура, которая возвращает рекордсет из нашей таблицы
create procedure dbo.Proc01
as
set nocount on
select * from dbo.Table01
go
--Вызов процедуры
exec dbo.Proc01



А теперь предположим, что нам нужно использовать результат этой процедуры, при этом нам нужно изменить название поля val на MyVal. Т.к. в текущих версиях мы это сделать напрямую не можем, то есть несколько обходных решений:

1) Одно из решений - это обернуть наш запрос в виде табличной функции
if object_id ( 'dbo.func01', 'TF' ) is not null
drop function dbo.func01
go
--Табличная функция
create function dbo.func01()
returns @tbl table (  id int
                    , MyVal varchar(50)
                    , dt datetime
                   )
as
begin
insert into @tbl
select * from dbo.Table01
return
end
go
--Выборка из нашей функции
select * from dbo.func01()



2) Ещё один вариант - это вывод результат процедуры во временную таблицу и уже после делать выборку уже из этой временной таблицы:
if object_id ( 'tempdb..#tmp_table', 'U' ) is not null
drop table #tmp_table
go
--Временная таблица, для загрузки рекордсета из нашей процедуры
create table #tmp_table ( id int 
                        , MyVal varchar(50)
                        , dt datetime
                        )
go
--Вставляем записи
insert into #tmp_table
exec dbo.Proc01
go
--Последующая выборка из временной таблицы
select * from #tmp_table



3) Третий способ - это использование функций OPENROWSET либо OPENQUERY
--Создадим линкед сервер, который ссылается на этот же сервер БД
exec master..sp_addlinkedserver N'(local)'
                              , N'SQL Server'
go
--Включаем настройку 'Ad Hoc Distributed Queries', т.к. иначе получим ошибку:
---------------------------------------
--Msg 15281, Level 16, State 1, Line 2
--SQL Server blocked access to STATEMENT 'OpenRowset/OpenDatasource' of component 'Ad Hoc Distributed Queries' 
--because this component is turned off as part of the security configuration for this server. 
--A system administrator can enable the use of 'Ad Hoc Distributed Queries' by using sp_configure. 
--For more information about enabling 'Ad Hoc Distributed Queries', search for 'Ad Hoc Distributed Queries' in SQL Server Books Online.
--------------------------------------- 
exec sp_configure 'show advanced options', 1
reconfigure
go
exec sp_configure 'Ad Hoc Distributed Queries', 1
reconfigure
go
--Выборка из процедуры, через OPENROWSET
select id, val as MyVal, dt from openrowset ( 'SQLNCLI'
                                            , 'Server=(local);Trusted_Connection=yes;'
                                            , 'exec test.dbo.Proc01'
                                            )
go
--Выборка из процедуры, через OPENQUERY
select id, val as MyVal, dt from openquery ( [(local)],  'exec test.dbo.Proc01' )

go



Ещё несколько вариантов можно подглядеть у Erland Sommarskog, SQL Server MVP - http://www.sommarskog.se/share_data.html

А теперь пример с использованием "фишек" SQL Denali:
exec dbo.Proc01
with result sets
 (
   ( id int
   , MyVal varchar(50)
   , dt datetime   
   )
 );



Но, как я уже писал выше, мы можем не только на лету менять имена полей, но и их тип:
exec dbo.Proc01
with result sets
 (
   ( id int
   , MyVal varchar(50)
   , dt date   
   )
 );





Ссылки по теме:

Tags: ,

SQL Server

Добавить комментарий

  Country flag

biuquote
  • Комментарий
  • Предпросмотр
Loading