web-dev-qa-db-ja.com

ストアドプロシージャの結果セットから列を選択する

私は80列と300行を返すストアドプロシージャを持っています。私はそれらの列のうち2つを取得する選択を書きたいと思います。何かのようなもの

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

上記の構文を使用した場合、エラーが発生します。

「無効な列名」.

私は最も簡単な解決策はストアドプロシージャを変更することであることを知っています、しかし私はそれを書きませんでした、そして私はそれを変更することができません。

私がやりたいことをする方法はありますか?

  • 結果を入れるための一時テーブルを作成することもできますが、80列あるので、2列を取得するには80列の一時テーブルを作成する必要があります。返されるすべての列を追跡しないようにしたいです。

  • Markが示唆しているようにWITH SprocResults AS ....を使ってみましたが、2つのエラーが出ました

    キーワード「EXEC」の近くに誤った構文があります。
    ')'付近の構文が正しくありません。

  • テーブル変数を宣言しようとしましたが、次のエラーが発生しました

    挿入エラー:列名または指定された値の数がテーブル定義と一致しません

  • 試してみると
    SELECT * FROM EXEC MyStoredProc 'param1', 'param2'
    エラーが出ます:

    キーワード 'exec'の近くの誤った構文.

412
Rossini

クエリを分割できますか?ストアドプロシージャの結果をテーブル変数または一時テーブルに挿入します。次に、テーブル変数から2つの列を選択します。

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

SELECT col1, col2 FROM @tablevar
172
Gulzar Nazim

ここにあなたの問題を解決するためのすべての異なる方法を説明するかなり良いドキュメントへのリンクがあります(それらの多くはあなたが既存のストアドプロシージャを修正することができないので使用できませんが)。

ストアドプロシージャ間でデータを共有する方法

Gulzarの答えは上手くいくでしょう(上のリンクで文書化されています)が、書くのは面倒です(@tablevar(col1、...)ステートメントに80個すべての列名を指定する必要があります)。スキーマに列が追加されたり、出力が変更されたりした場合は、コード内で列を更新する必要があります。そうしないとエラーになります。

81
Lance McNearney
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/

77
Peter Nazarov

これは私のために働きます:(すなわち私はsp_help_jobによって返された30+のうちの2つの列を必要とするだけです)

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 = '''+@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の方が好みです。

注2:以上のことをすべて言っておきます。通常はINSERT ... EXECを使用します。はい、10分の追加入力が必要ですが、手助けができるのであれば、次のようにしないでください。
(a)引用符の中の引用符の中の引用符、および
(b)sysテーブル、および/または卑劣な自己参照型リンクサーバーの設定(つまり、これらの場合は、私たちの強力なDBAに訴訟を申し立てる必要があります。)

ただし、この例では、sp_help_jobがすでに使用しているため、INSERT ... EXEC構文を使用できませんでした。 ( "INSERT EXECステートメントはネストできません。")

38
Merenzo

これがなぜそれほど難しいのかを知ることは役に立つかもしれません。ストアドプロシージャはテキスト(print 'text')のみを返すか、複数のテーブルを返すか、またはテーブルをまったく返さないことがあります。

だからSELECT * FROM (exec sp_tables) Table1のようなものは動作しません

10
newbie007

ストアドプロシージャを変更できる場合は、必要な列定義をパラメータとして簡単に配置して、自動作成された一時テーブルを使用できます。

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

この場合、一時テーブルを手動で作成する必要はありません。一時テーブルは自動的に作成されます。お役に立てれば。

9
dyatchenko

これを実現するには、まず以下のように#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
9
Navneet

(SQL Serverを想定)

T-SQLでストアドプロシージャの結果を操作する唯一の方法は、INSERT INTO ... EXEC構文を使用することです。これにより、一時テーブルまたはテーブル変数に挿入し、そこから必要なデータを選択することができます。

8
Brannon

データを手動で検証するためにこれを行っている場合は、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.co.jpSet.aspx

7
ShawnFeatherly

手っ取り早い方法は、新しいパラメータ'@Column_Name'を追加して、呼び出す関数に取得する列名を定義させることです。 sprocのreturn部分には、if/else文を指定して指定した列だけを返すか、空の場合はallを返します。

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

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;

それでおしまい!

6
LTMOD

質問で述べたように、ストアドプロシージャを実行する前に80列の一時テーブルを定義するのは困難です。

したがって、これを回避するもう1つの方法は、ストアドプロシージャの結果セットに基づいてテーブルにデータを入力することです。

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

これを試して

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

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

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
Emil

あなたが一度だけこれをする必要があるならばする最も簡単な方法:

インポート/エクスポートウィザードでExcelにエクスポートしてから、このExcelをテーブルにインポートします。

0
Martijn Tromp