Вставьте результаты хранимой процедуры во временную таблицу - PullRequest
1468 голосов
/ 17 марта 2009

Как мне сделать SELECT * INTO [temp table] FROM [stored procedure]? Не FROM [Table] и без определения [temp table]?

Select все данные из BusinessLine в tmpBusLine работают нормально.

select *
into tmpBusLine
from BusinessLine

Я пытаюсь сделать то же самое, но использование stored procedure, которое возвращает данные, не совсем то же самое.

select *
into tmpBusLine
from
exec getBusinessLineHistory '16 Mar 2009'

Выходное сообщение:

Сообщение 156, Уровень 15, Состояние 1, Строка 2 Неверный синтаксис рядом с ключевым словом 'Exec'.

Я прочитал несколько примеров создания временной таблицы с той же структурой, что и для хранимой процедуры вывода, которая работает нормально, но было бы неплохо не указывать столбцы.

Ответы [ 27 ]

29 голосов
/ 11 февраля 2010

Quassnoi поставил меня большую часть пути туда, но одна вещь отсутствовала:

**** Мне нужно было использовать параметры в хранимой процедуре. ****

И OPENQUERY не позволяет этому случиться:

Итак, я нашел способ работать с системой, а также не нужно делать определение таблицы настолько жестким и переопределять его внутри другой хранимой процедуры (и, конечно, воспользоваться возможностью, что она может сломаться)!

Да, вы можете динамически создать определение таблицы, возвращенное из хранимой процедуры, используя оператор OPENQUERY с фиктивными переменными (если NO RESULT SET возвращает такое же количество полей и в том же положении, что и набор данных с хорошими данными).

После создания таблицы вы можете использовать хранимую процедуру exec во временной таблице в течение всего дня.


И чтобы отметить (как указано выше), вы должны разрешить доступ к данным,

EXEC sp_serveroption 'MYSERVERNAME', 'DATA ACCESS', TRUE

Код:

declare @locCompanyId varchar(8)
declare @locDateOne datetime
declare @locDateTwo datetime

set @locDateOne = '2/11/2010'
set @locDateTwo = getdate()

--Build temporary table (based on bogus variable values)
--because we just want the table definition and
--since openquery does not allow variable definitions...
--I am going to use bogus variables to get the table defintion.

select * into #tempCoAttendanceRpt20100211
FROM OPENQUERY(DBASESERVER,
  'EXEC DATABASE.dbo.Proc_MyStoredProc 1,"2/1/2010","2/15/2010 3:00 pm"')

set @locCompanyId = '7753231'

insert into #tempCoAttendanceRpt20100211
EXEC DATABASE.dbo.Proc_MyStoredProc @locCompanyId,@locDateOne,@locDateTwo

set @locCompanyId = '9872231'

insert into #tempCoAttendanceRpt20100211
EXEC DATABASE.dbo.Proc_MyStoredProc @locCompanyId,@locDateOne,@locDateTwo

select * from #tempCoAttendanceRpt20100211
drop table #tempCoAttendanceRpt20100211

Спасибо за информацию, которая была предоставлена ​​изначально ... Да, наконец, мне не нужно создавать все эти поддельные (строгие) определения таблиц при использовании данных из другая хранимая процедура или база данных, и да вы также можете использовать параметры.

Поиск по тегу:

  • хранимая процедура SQL 2005 во временной таблице

  • openquery с хранимой процедурой и переменными 2005

  • открытый запрос с переменными

  • выполнить хранимую процедуру во временной таблице

Обновление: это не будет работать с временными таблицами , поэтому мне пришлось прибегнуть к созданию временной таблицы вручную.

Уведомление об обломе : это не будет работать с временными таблицами , http://www.sommarskog.se/share_data.html#OPENQUERY

Справка: Следующим шагом является определение LOCALSERVER. В примере это может выглядеть как ключевое слово, но на самом деле это только имя. Вот как вы это делаете:

sp_addlinkedserver @server = 'LOCALSERVER',  @srvproduct = '',
                   @provider = 'SQLOLEDB', @datasrc = @@servername

Чтобы создать связанный сервер, вы должны иметь разрешение ALTER ANY SERVER или быть членом какой-либо из фиксированных ролей сервера sysadmin или setupadmin.

OPENQUERY открывает новое соединение с SQL Server. Это имеет некоторые последствия:

Процедура, которую вы вызываете с помощью OPENQUERY, не может ссылаться на временные таблицы, созданные в текущем соединении.

Новое соединение имеет собственную базу данных по умолчанию (определенную с помощью sp_addlinkedserver, по умолчанию - master), поэтому все спецификации объекта должны включать имя базы данных.

Если у вас есть открытая транзакция и вы удерживаете блокировки при вызове OPENQUERY, вызываемая процедура не может получить доступ к тому, что вы заблокировали. То есть, если вы не будете осторожны, вы заблокируете себя.

Подключение не бесплатное, поэтому снижается производительность.

26 голосов
/ 05 февраля 2014

Если OPENROWSET вызывает у вас проблемы, есть другой способ, начиная с 2012 года; использовать sys.dm_exec_describe_first_result_set_for_object, как упомянуто здесь: Получить имена столбцов и типы хранимых процедур?

Сначала создайте эту хранимую процедуру, чтобы сгенерировать SQL для временного

CREATE PROCEDURE dbo.usp_GetStoredProcTableDefinition(
    @ProcedureName  nvarchar(128),
    @TableName      nvarchar(128),
    @SQL            nvarchar(max) OUTPUT
)
AS
SET @SQL = 'CREATE TABLE ' + @tableName + ' ('

SELECT @SQL = @SQL + '['+name +'] '+ system_type_name +''  + ','
        FROM sys.dm_exec_describe_first_result_set_for_object
        (
          OBJECT_ID(@ProcedureName), 
          NULL
        );

--Remove trailing comma
SET @SQL = SUBSTRING(@SQL,0,LEN(@SQL))    
SET @SQL =  @SQL +')'

Чтобы использовать процедуру, вызовите ее следующим образом:

DECLARE     @SQL    NVARCHAR(MAX)

exec dbo.usp_GetStoredProcTableDefinition
    @ProcedureName='dbo.usp_YourProcedure',
    @TableName='##YourGlobalTempTable',@SQL = @SQL OUTPUT

INSERT INTO ##YourGlobalTempTable
EXEC    [dbo].usp_YourProcedure

select * from ##YourGlobalTempTable

Обратите внимание, что я использую глобальную временную таблицу. Это связано с тем, что при использовании EXEC для запуска динамического SQL создается собственный сеанс, поэтому обычная временная таблица выходит за рамки любого последующего кода. Если глобальная временная таблица является проблемой, вы можете использовать обычную временную таблицу, но любой последующий SQL должен быть динамическим, то есть также выполняемым оператором EXEC.

21 голосов
/ 26 сентября 2011

Этот сохраненный процесс выполняет работу:

CREATE PROCEDURE [dbo].[ExecIntoTable]
(
    @tableName          NVARCHAR(256),
    @storedProcWithParameters   NVARCHAR(MAX)
)
AS
BEGIN
    DECLARE @driver         VARCHAR(10)
    DECLARE @connectionString   NVARCHAR(600)
    DECLARE @sql            NVARCHAR(MAX)
    DECLARE @rowsetSql      NVARCHAR(MAX)

    SET @driver = '''SQLNCLI'''

    SET @connectionString = 
        '''server=' + 
            CAST(SERVERPROPERTY('ServerName') AS NVARCHAR(256)) + 
            COALESCE('\' + CAST(SERVERPROPERTY('InstanceName') AS NVARCHAR(256)), '') + 
        ';trusted_connection=yes'''

    SET @rowsetSql = '''EXEC ' + REPLACE(@storedProcWithParameters, '''', '''''') + ''''

    SET @sql = '
SELECT
    *
INTO 
    ' + @tableName + ' 
FROM
    OPENROWSET(' + @driver + ',' + @connectionString + ',' + @rowsetSql + ')'

    EXEC (@sql)
END
GO

Это небольшая переделка: Вставьте результаты хранимой процедуры в таблицу , чтобы она действительно работала.

Если вы хотите, чтобы он работал с временной таблицей, вам нужно будет использовать таблицу ##GLOBAL и затем удалить ее.

18 голосов
/ 26 февраля 2016

Если вам повезло с SQL 2012 или выше, вы можете использовать dm_exec_describe_first_result_set_for_object

Я только что отредактировал sql, предоставленный gotqn. Спасибо, получил.

Это создает глобальную временную таблицу с именем, совпадающим с именем процедуры. Временная таблица может позже использоваться по мере необходимости. Только не забудьте сбросить его перед повторным выполнением.

    declare @procname nvarchar(255) = 'myProcedure',
            @sql nvarchar(max) 

    set @sql = 'create table ##' + @procname + ' ('
    begin
            select      @sql = @sql + '[' + r.name + '] ' +  r.system_type_name + ','
            from        sys.procedures AS p
            cross apply sys.dm_exec_describe_first_result_set_for_object(p.object_id, 0) AS r
            where       p.name = @procname

            set @sql = substring(@sql,1,len(@sql)-1) + ')'
            execute (@sql)
            execute('insert ##' + @procname + ' exec ' + @procname)
    end
16 голосов
/ 05 октября 2013
  1. Я создаю таблицу со следующей схемой и данными.
  2. Создание хранимой процедуры.
  3. Теперь я знаю, каков результат моей процедуры, поэтому я выполняю следующий запрос.

    CREATE TABLE [dbo].[tblTestingTree](
        [Id] [int] IDENTITY(1,1) NOT NULL,
        [ParentId] [int] NULL,
        [IsLeft] [bit] NULL,
        [IsRight] [bit] NULL,
    CONSTRAINT [PK_tblTestingTree] PRIMARY KEY CLUSTERED
    (
        [Id] ASC
    ) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    GO
    SET IDENTITY_INSERT [dbo].[tblTestingTree] ON
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (1, NULL, NULL, NULL)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (2, 1, 1, NULL)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (3, 1, NULL, 1)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (4, 2, 1, NULL)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (5, 2, NULL, 1)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (6, 3, 1, NULL)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (7, 3, NULL, 1)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (8, 4, 1, NULL)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (9, 4, NULL, 1)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (10, 5, 1, NULL)
    
    SET IDENTITY_INSERT [dbo].[tblTestingTree] OFF
    

    ЗНАЧЕНИЯ (10, 5, 1, NULL) SET IDENTITY_INSERT [dbo]. [TblTestingTree] Вкл.

    create procedure GetDate
    as
    begin
        select Id,ParentId from tblTestingTree
    end
    
    create table tbltemp
    (
        id int,
        ParentId int
    )
    insert into tbltemp
    exec GetDate
    
    select * from tbltemp;
    
15 голосов
/ 03 июня 2015

Чтобы вставить первый набор записей хранимой процедуры во временную таблицу, вам необходимо знать следующее:

  1. во временную таблицу может быть вставлен только первый набор строк хранимой процедуры
  2. хранимая процедура не должна выполнять динамический оператор T-SQL (sp_executesql)
  3. сначала нужно определить структуру временной таблицы

Вышеупомянутое может выглядеть как ограничение, но ИМХО это вполне имеет смысл - если вы используете sp_executesql, вы можете один раз вернуть два столбца и один раз десять, а если у вас есть несколько наборов результатов, вы не можете вставить их в несколько таблиц как хорошо - вы можете вставить максимум в две таблицы в одном операторе T-SQL (используя предложение OUTPUT без триггеров).

Итак, проблема в основном в том, как определить структуру временной таблицы перед выполнением оператора EXEC ... INTO ....

Первый работает с OBJECT_ID, в то время как второй и третий работают также со специальными запросами. Я предпочитаю использовать DMV вместо sp, поскольку вы можете использовать CROSS APPLY и создавать временные определения таблиц для нескольких процедур одновременно.

SELECT p.name, r.* 
FROM sys.procedures AS p
CROSS APPLY sys.dm_exec_describe_first_result_set_for_object(p.object_id, 0) AS r;

Также обратите внимание на поле system_type_name, так как оно может быть очень полезным. В нем хранится полное определение столбца. Например:

smalldatetime
nvarchar(max)
uniqueidentifier
nvarchar(1000)
real
smalldatetime
decimal(18,2)

и вы можете использовать его напрямую в большинстве случаев для создания определения таблицы.

Итак, я думаю, что в большинстве случаев (если хранимая процедура соответствует определенным критериям) вы можете легко построить динамические операторы для решения таких проблем (создать временную таблицу, вставить в нее результат хранимой процедуры, сделать то, что вам нужно с данные).


Обратите внимание, что вышеприведенные объекты не могут определить данные первого результирующего набора в некоторых случаях , например, когда выполняются динамические операторы T-SQL или временные таблицы используются в хранимой процедуре.

14 голосов
/ 07 сентября 2015

Если запрос не содержит параметра, используйте OpenQuery, иначе используйте OpenRowset.

Основная вещь заключается в создании схемы в соответствии с хранимой процедурой и вставке в эту таблицу. e.g.:

DECLARE @abc TABLE(
                  RequisitionTypeSourceTypeID INT
                , RequisitionTypeID INT
                , RequisitionSourcingTypeID INT
                , AutoDistOverride INT
                , AllowManagerToWithdrawDistributedReq INT
                , ResumeRequired INT
                , WarnSupplierOnDNRReqSubmission  INT
                , MSPApprovalReqd INT
                , EnableMSPSupplierCounterOffer INT
                , RequireVendorToAcceptOffer INT
                , UseCertification INT
                , UseCompetency INT
                , RequireRequisitionTemplate INT
                , CreatedByID INT
                , CreatedDate DATE
                , ModifiedByID INT
                , ModifiedDate DATE
                , UseCandidateScheduledHours INT
                , WeekEndingDayOfWeekID INT
                , AllowAutoEnroll INT
                )
INSERT INTO @abc
EXEC [dbo].[usp_MySp] 726,3
SELECT * FROM @abc
11 голосов
/ 06 августа 2009

Я нашел Передача массивов / таблиц данных в хранимые процедуры , что может дать вам другое представление о том, как вы можете решить свою проблему.

Ссылка предлагает использовать параметр типа Image для передачи в хранимую процедуру. Затем в хранимой процедуре изображение преобразуется в табличную переменную, содержащую исходные данные.

Может быть, есть способ, которым это можно использовать с временной таблицей.

11 голосов
/ 10 марта 2011

Код

CREATE TABLE #T1
(
    col1 INT NOT NULL,
    col2 NCHAR(50) NOT NULL,
    col3 TEXT NOT NULL,
    col4 DATETIME NULL,
    col5 NCHAR(50) NULL,
    col6 CHAR(2) NULL,
    col6 NCHAR(100) NULL,
    col7 INT NULL,
    col8 NCHAR(50) NULL,
    col9 DATETIME NULL,
    col10 DATETIME NULL
)

DECLARE @Para1 int
DECLARE @Para2 varchar(32)
DECLARE @Para3 varchar(100)
DECLARE @Para4 varchar(15)
DECLARE @Para5 varchar (12)
DECLARE @Para6 varchar(1)
DECLARE @Para7 varchar(1)


SET @Para1 = 1025
SET @Para2 = N'6as54fsd56f46sd4f65sd'
SET @Para3 = N'XXXX\UserName'
SET @Para4 = N'127.0.0.1'
SET @Para5 = N'XXXXXXX'
SET @Para6 = N'X'
SET @Para7 = N'X'

INSERT INTO #T1
(
    col1,
    col2,
    col3,
    col4,
    col5,
    col6,
    col6,
    col7,
    col8,
    col9,
    col10,
)
EXEC [dbo].[usp_ProcedureName] @Para1, @Para2, @Para3, @Para4, @Para5, @Para6, @Para6

Надеюсь, это поможет. Пожалуйста, укажите подходящее.

9 голосов
/ 18 августа 2014

Я столкнулся с той же проблемой, и вот что я сделал для этого из предложения Павла . Основная часть здесь заключается в том, чтобы использовать NEWID(), чтобы избежать одновременного запуска процедур / скриптов несколькими пользователями, что создает боль для глобальной временной таблицы.

DECLARE @sql varchar(max) = '', 
@tmp_global_table varchar(255) = '##global_tmp_' + CONVERT(varchar(36), NEWID())
SET @sql = @sql + 'select * into [' + @tmp_global_table + '] from YOURTABLE'
EXEC(@sql)

EXEC('SELECT * FROM [' + @tmp_global_table + ']')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...