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

У меня есть хранимая процедура, которая возвращает 80 столбцов и 300 строк. Я хочу написать select, который получает 2 из этих столбцов. Что-то вроде

SELECT col1, col2 FROM EXEC MyStoredProc 'param1', 'param2'

Когда я использовал приведенный выше синтаксис, я получаю ошибку:

"Недопустимое имя столбца".

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

Есть ли способ сделать то, что я хочу?

  • Я могу создать временную таблицу для ввода результатов, но потому, что есть 80 столбцов, поэтому мне нужно будет создать таблицу столбцов 80 столбцов, чтобы получить 2 столбца. Я хотел избежать отслеживания всех возвращаемых столбцов.

  • Я попытался использовать WITH SprocResults AS ...., как предложил Марк, но я получил 2 ошибки

    Неправильный синтаксис рядом с ключевым словом 'EXEC'.
    Неверный синтаксис рядом с ')'.

  • Я попытался объявить переменную таблицы, и я получил следующую ошибку:

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

  • Если я попробую
    SELECT * FROM EXEC MyStoredProc 'param1', 'param2'
    Я получаю сообщение об ошибке:

    Неправильный синтаксис рядом с ключевым словом "exec".

Ответ 1

Можете ли вы разделить запрос? Вставьте сохраненные результаты процедуры в табличную переменную или временную таблицу. Затем выберите 2 столбца из табличной переменной.

Declare @tablevar table(col1 col1Type,..
insert into @tablevar(col1,..) exec MyStoredProc 'param1', 'param2'

SELECT col1, col2 FROM @tablevar

Ответ 2

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

Как делиться данными между хранимыми процедурами

Ответ Gulzar будет работать (он задокументирован в ссылке выше), но это будет хлопот для написания (вам нужно указать все 80 имен столбцов в вашем описании @tablevar (col1,...). в будущем, если столбец будет добавлен в схему или будет изменен вывод, он должен быть обновлен в вашем коде или он выйдет из строя.

Ответ 4

Это работает для меня: (т.е. мне нужны только 2 столбца из 30+, возвращаемых sp_help_job)

SELECT name, current_execution_status 
FROM OPENQUERY (MYSERVER, 
  'EXEC msdb.dbo.sp_help_job @job_name = ''My Job'', @job_aspect = ''JOB''');  

Прежде чем это сработает, мне нужно было запустить это:

sp_serveroption 'MYSERVER', 'DATA ACCESS', TRUE;

.... обновить таблицу sys.servers. (т.е. использование самооценки внутри OPENQUERY по умолчанию отключено.)

Для моего простого требования я не столкнулся ни с одной из проблем, описанных в разделе OPENQUERY в превосходной ссылке Lance.

Rossini, если вам нужно динамически устанавливать эти входные параметры, то использование OPENQUERY становится немного более неудобно:

DECLARE @innerSql varchar(1000);
DECLARE @outerSql varchar(1000);

-- Set up the original stored proc definition.
SET @innerSql = 
'EXEC msdb.dbo.sp_help_job @job_name = '''[email protected]+''', @job_aspect = N'''[email protected]+'''' ;

-- Handle quotes.
SET @innerSql = REPLACE(@innerSql, '''', '''''');

-- Set up the OPENQUERY definition.
SET @outerSql = 
'SELECT name, current_execution_status 
FROM OPENQUERY (MYSERVER, ''' + @innerSql + ''');';

-- Execute.
EXEC (@outerSql);

Я не уверен в различиях (если таковые имеются) между использованием sp_serveroption для непосредственного обновления существующей самооценки sys.servers, а также с помощью sp_addlinkedserver (как описано в ссылке Лэнса) для создания дубликата/псевдоним.

Примечание 1: Я предпочитаю OPENQUERY над OPENROWSET, учитывая, что OPENQUERY не требует определения строки подключения в proc.

Примечание 2: Сказав все это: обычно я просто использую INSERT... EXEC:) Да, это 10 минут дополнительной набрав, но если я могу помочь, я предпочитаю не смеяться: (a) цитирует кавычки в кавычках и
(b) sys-таблицы и/или скрытые самонастраивающиеся настройки Linked Server (т.е. для этого мне нужно просить мое дело для наших всемогущих администраторов баз данных:)

Однако в этом случае я не мог использовать конструкцию INSERT... EXEC, поскольку sp_help_job уже использует ее. ( "Инструкция INSERT EXEC не может быть вложенной." )

Ответ 5

Возможно, было бы полезно узнать, почему это так сложно. Хранимая процедура может возвращать только текст (печатать "текст" ) или может возвращать несколько таблиц или вообще не может возвращать таблицы.

Так что что-то вроде SELECT * FROM (exec sp_tables) Table1 не будет работать

Ответ 6

Для этого сначала создайте #test_table, как показано ниже:

create table #test_table(
    col1 int,
    col2 int,
   .
   .
   .
    col80 int
)

Теперь выполните процедуру и поместите значение в #test_table:

insert into #test_table
EXEC MyStoredProc 'param1', 'param2'

Теперь вы получаете значение из #test_table:

select col1,col2....,col80 from #test_table

Ответ 7

Если вы можете изменить хранимую процедуру, вы можете легко установить необходимые определения столбцов в качестве параметра и использовать автоматически созданную временную таблицу:

CREATE PROCEDURE sp_GetDiffDataExample
      @columnsStatement NVARCHAR(MAX) -- required columns statement (e.g. "field1, field2")
AS
BEGIN
    DECLARE @query NVARCHAR(MAX)
    SET @query = N'SELECT ' + @columnsStatement + N' INTO ##TempTable FROM dbo.TestTable'
    EXEC sp_executeSql @query
    SELECT * FROM ##TempTable
    DROP TABLE ##TempTable
END

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

Ответ 8

(Предполагая SQL Server)

Единственный способ работать с результатами хранимой процедуры в T-SQL - использовать синтаксис INSERT INTO ... EXEC. Это дает вам возможность вставить в временную таблицу или переменную таблицы и оттуда выбрать нужные вам данные.

Ответ 9

Быстрое взломать было бы добавить новый параметр '@Column_Name' и вызвать функцию вызова для определения имени столбца. В возвращаемой части вашего sproc вы должны иметь операторы if else и возвращать только указанный столбец, или если пусто - вернуть все.

CREATE PROCEDURE [dbo].[MySproc]
        @Column_Name AS VARCHAR(50)
AS
BEGIN
    IF (@Column_Name = 'ColumnName1')
        BEGIN
            SELECT @ColumnItem1 as 'ColumnName1'
        END
    ELSE
        BEGIN
            SELECT @ColumnItem1 as 'ColumnName1', @ColumnItem2 as 'ColumnName2', @ColumnItem3 as 'ColumnName3'
        END
END

Ответ 10

Если вы делаете это для ручной проверки данных, вы можете сделать это с помощью LINQPad.

Создайте соединение с базой данных в LinqPad, затем создайте операторы С#, подобные следующим:

DataTable table = MyStoredProc (param1, param2).Tables[0];
(from row in table.AsEnumerable()
 select new
 {
  Col1 = row.Field<string>("col1"),
  Col2 = row.Field<string>("col2"),
 }).Dump();

Ссылка http://www.global-webnet.net/blogengine/post/2008/09/10/LINQPAD-Using-Stored-Procedures-Accessing-a-DataSet.aspx

Ответ 11

Для SQL Server я считаю, что это отлично работает:

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

Вот пример:

DECLARE @temp TABLE (firstname NVARCHAR(30), lastname nvarchar(50));

INSERT INTO @temp EXEC dbo.GetPersonName @param1,@param2;
-- assumption is that dbo.GetPersonName returns a table with firstname / lastname columns

SELECT * FROM @temp;

Что это!

Ответ 12

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

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

SELECT * INTO #temp FROM OPENROWSET('SQLNCLI', 'Server=localhost;Trusted_Connection=yes;'
                                   ,'EXEC MyStoredProc')

Если вы получаете какую-либо ошибку, вам нужно включить специальные распределенные запросы, выполнив следующий запрос.

sp_configure 'Show Advanced Options', 1
GO
RECONFIGURE
GO
sp_configure 'Ad Hoc Distributed Queries', 1
GO
RECONFIGURE
GO

Чтобы выполнить sp_configure с обоими параметрами, чтобы изменить параметр конфигурации или запустить оператор RECONFIGURE, вам должно быть предоставлено разрешение ALTER SETTINGS на уровне сервера

Теперь вы можете выбрать свои конкретные столбцы из сгенерированной таблицы

SELECT col1, col2
FROM #temp

Ответ 13

попробуйте это

use mydatabase
create procedure sp_onetwothree as
select 1 as '1', 2 as '2', 3 as '3'
go
SELECT a.[1], a.[2]
FROM OPENROWSET('SQLOLEDB','myserver';'sa';'mysapass',
    'exec mydatabase.dbo.sp_onetwothree') AS a
GO

Ответ 14

Самый простой способ сделать это, если вам нужно только одно:

Экспортировать в excel в мастер импорта и экспорта, а затем импортировать это excel в таблицу.

Ответ 15

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

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

Примечание: есть гораздо лучшие, более универсальные решения, которые будут работать с меньшим количеством строк кода, если вы захотите/сможете обновить SP или изменить конфигурацию и использовать OPENROWSET. Используйте ниже, если у вас нет другого пути.

DECLARE @spName VARCHAR(MAX) = 'MyStoredProc'
DECLARE @tempTableName VARCHAR(MAX) = '#tempTable'

-- might need to update this if your param value is a string and you need to escape quotes
DECLARE @insertCommand VARCHAR(MAX) = 'INSERT INTO ' + @tempTableName + ' EXEC MyStoredProc @param=value'

DECLARE @createTableCommand VARCHAR(MAX)

-- update this to select the columns you want
DECLARE @selectCommand VARCHAR(MAX) = 'SELECT col1, col2 FROM ' + @tempTableName

DECLARE @dropCommand VARCHAR(MAX) = 'DROP TABLE ' + @tempTableName

-- Generate command to create temp table
SELECT @createTableCommand = 'CREATE TABLE ' + @tempTableName + ' (' +
    STUFF
    (
        (
            SELECT ', ' + CONCAT('[', name, ']', ' ', system_type_name)
            FROM sys.dm_exec_describe_first_result_set_for_object
            (
              OBJECT_ID(@spName), 
              NULL
            )
            FOR XML PATH('')
        )
        ,1
        ,1
        ,''
    ) + ')'

EXEC( @createTableCommand + ' '+ @insertCommand + ' ' + @selectCommand + ' ' + @dropCommand)

Ответ 16

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

Declare @sql nvarchar(max)
Set @sql='SELECT   col1, col2 FROM OPENROWSET(''SQLNCLI'', ''Server=(local);uid=test;pwd=test'',
     ''EXEC MyStoredProc ''''param1'''', ''''param2'''''')'
 Exec(@sql)

если у вас есть доверенное соединение, используйте следующую инструкцию запроса:

Declare @sql nvarchar(max)
Set @sql='SELECT   col1, col2 FROM OPENROWSET(''SQLNCLI'', ''Server=(local);Trusted_Connection=yes;'',
     ''EXEC MyStoredProc ''''param1'''', ''''param2'''''')'
 Exec(@sql)

если вы получаете ошибку при выполнении вышеприведенного оператора, просто запустите этот оператор ниже:

sp_configure 'Show Advanced Options', 1
GO
RECONFIGURE
GO
sp_configure 'Ad Hoc Distributed Queries', 1
GO
RECONFIGURE
GO

Я надеюсь, что это поможет кому-то, кто столкнулся с подобной проблемой. Если кто-то попытается использовать временную таблицу или переменную таблицы, которая должна быть такой, как показано ниже, но в этом сценарии вы должны знать, сколько столбцов возвращает sp, тогда вам следует создать столько столбцов во временной таблице или переменной:

--for table variable 
Declare @t table(col1 col1Type, col2 col2Type)
insert into @t exec MyStoredProc 'param1', 'param2'
SELECT col1, col2 FROM @t

--for temp table
create table #t(col1 col1Type, col2 col2Type)
insert into #t exec MyStoredProc 'param1', 'param2'
SELECT col1, col2 FROM #t

Ответ 17

Я бы вырезал и вставлял оригинальный SP и удалял все столбцы, кроме 2, которые вы хотите. Или. Я вернул бы результат, вернул бы его в соответствующий бизнес-объект, а затем LINQ из двух столбцов.