Виберіть стовпці з набору результатів збереженої процедури


448

У мене зберігається процедура, яка повертає 80 стовпців і 300 рядків. Я хочу написати вибір, який отримує 2 з цих стовпців. Щось на зразок

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

Коли я використовував вищевказаний синтаксис, я отримую помилку:

"Недійсна назва стовпця".

Я знаю, що найпростішим рішенням було б змінити збережену процедуру, але я її не написав і не можу її змінити.

Чи є спосіб зробити те, що я хочу?

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

  • Я спробував використовувати, WITH SprocResults AS ....як запропонував Марк, але у мене виникли 2 помилки

    Неправильний синтаксис біля ключового слова "EXEC".
    Неправильний синтаксис біля ')'.

  • Я спробував оголосити змінну таблиці і отримав таку помилку

    Помилка вставки: Назва стовпця або кількість наданих значень не відповідають визначенню таблиці

  • Якщо я спробую,
    SELECT * FROM EXEC MyStoredProc 'param1', 'param2'
    я отримую помилку:

    Неправильний синтаксис біля ключового слова "exec".


Чи не цікаво, чи працює цей запит: SELECT * FROM EXEC MyStoredProc 'param1', 'param2' Якщо так, то які назви стовпців відображаються в наборі результатів, і чи можете ви використовувати ці назви стовпців у списку вибору?
Дейв Коста

5
Я ніколи не знайшов відповіді на це.
Россіні

32
Ну, ви ніколи не відповідали на дуже важливе запитання! Про яку платформу SQL ви питаєте? MySQL, Microsoft SQL Server, Oracle тощо. Мені здається, це SQL Server, але вам потрібно сказати людям, або вони не можуть надійно відповісти на ваше запитання.
JMTyler

6
Ну, це повинен бути MS-SQL. EXECне є ключовим словом MySQL (еквівалент MySQL підготовлений операторами ). Хоча я хотів би знати відповідь для MySQL, відповіді нижче націлені на T-SQL. Повторне позначення.
бобобобо

1
Я ніколи не знайшов відповіді на це
Россіні

Відповіді:


186

Чи можете ви розділити запит? Вставте збережені результати протоколу в змінну таблиці або темп. Потім виберіть 2 стовпці зі змінної таблиці.

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

SELECT col1, col2 FROM @tablevar

27
Це також не працює, коли ви не знаєте визначення таблиці
Ian Boyd

не знав про цей тип. Чи реалізовані вони так само, як і тимчасові таблиці? Або це суворо в пам’яті?
d -_- b

Це було цікаво: sqlnerd.blogspot.com/2005/09/…
d -_- b

Це працює нормально, якщо кількість стовпців, що подаються у таблиці темпів, така сама, як і у висновку збереженої процедури. чагбер.
Чагберт

83

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

Як поділитися даними між збереженими процедурами

Відповідь Гульзара спрацює (це зафіксовано за посиланням вище), але писати буде неприємно (вам потрібно буде вказати всі 80 імен стовпців у своєму заяві @tablevar (col1, ...). І в майбутньому якщо до схеми додано стовпець або змінено вихід, його потрібно буде оновити у вашому коді, або він вийде з помилки.


1
Я думаю, що пропозиція про відкриття в цьому посиланні набагато ближче до того, що шукає ОП.
Корин

80
CREATE TABLE #Result
(
  ID int,  Name varchar(500), Revenue money
)
INSERT #Result EXEC RevenueByAdvertiser '1/1/10', '2/1/10'
SELECT * FROM #Result ORDER BY Name
DROP TABLE #Result

Джерело:
http://stevesmithblog.com/blog/select-from-a-stored-procedure/


@LawfulHacker Holy курить. Що ви робите на SQL Server 2000 у 2014 році?
Іван Заброський

1
Великі корпорації зі застарілими системами: D
LawfulHacker

39

Це працює для мене: (тобто мені потрібні лише 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 стає дещо прискіпливішим:

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 = '''+@param1+''', @job_aspect = N'''+@param2+'''' ;

-- 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 хвилин додатковий набір тексту, але якщо я можу допомогти йому, я вважаю за краще не джіггер навколо з:
(а) лапки всередині лапок усередині цитати та
(b) систематизовані таблиці та / або приховані самонавірені установки пов’язаного сервера (тобто для них мені потрібно подати свою справу до наших всемогутніх DBA :)

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


3
У мене було 13 одиночних лапок підряд у програмі dinami-sql-що-генеровано-динамічно-sql-що-генерується-динамічне-sql ...
ErikE

Мені потрібно перевірити, чи робота закінчена. "Оператор INSERT EXEC не може бути вкладений". Я ненавиджу вас Microsoft.
алельковельський

11

Щоб досягти цього, спочатку створіть #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

2
Чи є перевага створити таблицю темп замість змінної таблиці?
Дейв Келлі

1
найкраще рішення, яке я знайшов на stackoverflow! :)
Умар

4
Що робити, якщо мені потрібен лише один стовпець з іншого збереженої процедури?
Кеваль Патель

11

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

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

У цьому випадку вам не потрібно створювати темп-таблицю вручну - вона створюється автоматично. Сподіваюсь, це допомагає.


Будьте обережні, використовуючи ## таблиці, оскільки вони діляться між сесіями
eKelvin

1
Ви можете знайти короткий опис відмінностей між # та ## таблицями в stackoverflow.com/a/34081438/352820
eKelvin

Це схильне до ін'єкції SQL?
Бушрод

11

Можливо, буде корисно дізнатися, чому це так складно. Збережена процедура може повертати лише текст (друкувати "текст"), або може повертати кілька таблиць, або взагалі не повертати ніяких таблиць.

Тож щось подібне SELECT * FROM (exec sp_tables) Table1 не вийде


12
SQL Server може вільно помилитися, якщо це трапиться. наприклад, якщо я пишу підзапит, який повертає більше ніж одне значення. Так, це може статися, але насправді це не так. І навіть якщо це було так: не важко підняти помилку.
Ян Бойд

8

(Припустимо, SQL Server)

Єдиний спосіб роботи з результатами збереженої процедури в T-SQL - це використання INSERT INTO ... EXECсинтаксису. Це дає вам можливість вставити в тимчасову таблицю або змінну таблиці і звідти вибрати потрібні вам дані.


1
Для цього потрібно знати визначення таблиці. Не корисно.
Трайнко

8

Швидкий злом був би додати новий параметр '@Column_Name'і функцію виклику визначати назву стовпця, який потрібно отримати. У частині, що повертається, ви матимете оператори 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

7

Якщо ви робите це для ручної перевірки даних, ви можете це зробити за допомогою LINQPad.

Створіть підключення до бази даних в LinqPad, потім створіть C # заяви, подібні до наступного:

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


7

Для 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;

Це воно!


Для цього потрібно створити копію визначення таблиці. Чи є спосіб уникнути цього?
Хардік

7

Як уже згадувалося в питанні, важко визначити таблицю темп 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

4

спробуйте це

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

1
Лол - він цього не зробив. Він зашифрував це у виклику збереженої процедури, а не там, де її можна набагато легше отримати без доступу до бази даних шляхом нюхання мережі.
Мартін Мілан

Досить легко дістати його і з Github.
номен.

3

Я знаю, що виконання з 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, тоді вам слід створити стільки стовпців у таблиці temp або змінній таблиці:

--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

1

Для всіх, у кого є 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)

0

Найпростіший спосіб зробити це, якщо вам це потрібно лише один раз:

Експортуйте у майстер імпорту та експорту в Excel, а потім імпортуйте цю програму в таблицю.


4
Весь сенс створення збереженої програми - це повторне використання. Ваша відповідь повністю суперечить цьому.
deutschZuid

6
Щоб протидіяти deutschZuid, в оригінальному дописі він не згадує, чи хоче він повторно використовувати це, чи просто намагається переглянути результати збереженої програми. Мартін правий, це, мабуть, найпростіший спосіб, якщо йому потрібно це зробити лише один раз.
Єпископ

0

Створіть динамічний вигляд і отримайте результат від нього .......

CREATE PROCEDURE dbo.usp_userwise_columns_value
(
    @userid BIGINT
)
AS 
BEGIN
        DECLARE @maincmd NVARCHAR(max);
        DECLARE @columnlist NVARCHAR(max);
        DECLARE @columnname VARCHAR(150);
        DECLARE @nickname VARCHAR(50);

        SET @maincmd = '';
        SET @columnname = '';
        SET @columnlist = '';
        SET @nickname = '';

        DECLARE CUR_COLUMNLIST CURSOR FAST_FORWARD
        FOR
            SELECT columnname , nickname
            FROM dbo.v_userwise_columns 
            WHERE userid = @userid

        OPEN CUR_COLUMNLIST
        IF @@ERROR <> 0
            BEGIN
                ROLLBACK
                RETURN
            END   

        FETCH NEXT FROM CUR_COLUMNLIST
        INTO @columnname, @nickname

        WHILE @@FETCH_STATUS = 0
            BEGIN
                SET @columnlist = @columnlist + @columnname + ','

                FETCH NEXT FROM CUR_COLUMNLIST
                INTO @columnname, @nickname
            END
        CLOSE CUR_COLUMNLIST
        DEALLOCATE CUR_COLUMNLIST  

        IF NOT EXISTS (SELECT * FROM sys.views WHERE name = 'v_userwise_columns_value')
            BEGIN
                SET @maincmd = 'CREATE VIEW dbo.v_userwise_columns_value AS SELECT sjoid, CONVERT(BIGINT, ' + CONVERT(VARCHAR(10), @userid) + ') as userid , ' 
                            + CHAR(39) + @nickname + CHAR(39) + ' as nickname, ' 
                            + @columnlist + ' compcode FROM dbo.SJOTran '
            END
        ELSE
            BEGIN
                SET @maincmd = 'ALTER VIEW dbo.v_userwise_columns_value AS SELECT sjoid, CONVERT(BIGINT, ' + CONVERT(VARCHAR(10), @userid) + ') as userid , ' 
                            + CHAR(39) + @nickname + CHAR(39) + ' as nickname, ' 
                            + @columnlist + ' compcode FROM dbo.SJOTran '
            END

        --PRINT @maincmd
        EXECUTE sp_executesql @maincmd
END

-----------------------------------------------
SELECT * FROM dbo.v_userwise_columns_value

-1

Я вирізав би і вставив оригінальний SP та видалив усі стовпці, крім 2, які потрібно. Або Я поверну набір результатів назад, відзначте його на належному бізнес-об'єкті, а потім LINQ виберіть два стовпці.


1
Люди цього не роблять. Це призведе до порушення принципу DRY. Коли все зміниться, якщо не, то вам доведеться відстежувати та вносити зміни у всіх місцях.
jriver27
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.