私は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'の近くの誤った構文.
クエリを分割できますか?ストアドプロシージャの結果をテーブル変数または一時テーブルに挿入します。次に、テーブル変数から2つの列を選択します。
Declare @tablevar table(col1 col1Type,..
insert into @tablevar(col1,..) exec MyStoredProc 'param1', 'param2'
SELECT col1, col2 FROM @tablevar
ここにあなたの問題を解決するためのすべての異なる方法を説明するかなり良いドキュメントへのリンクがあります(それらの多くはあなたが既存のストアドプロシージャを修正することができないので使用できませんが)。
Gulzarの答えは上手くいくでしょう(上のリンクで文書化されています)が、書くのは面倒です(@tablevar(col1、...)ステートメントに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/
これは私のために働きます:(すなわち私は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ステートメントはネストできません。")
これがなぜそれほど難しいのかを知ることは役に立つかもしれません。ストアドプロシージャはテキスト(print 'text')のみを返すか、複数のテーブルを返すか、またはテーブルをまったく返さないことがあります。
だからSELECT * FROM (exec sp_tables) Table1
のようなものは動作しません
ストアドプロシージャを変更できる場合は、必要な列定義をパラメータとして簡単に配置して、自動作成された一時テーブルを使用できます。
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
この場合、一時テーブルを手動で作成する必要はありません。一時テーブルは自動的に作成されます。お役に立てれば。
これを実現するには、まず以下のように#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
(SQL Serverを想定)
T-SQLでストアドプロシージャの結果を操作する唯一の方法は、INSERT INTO ... EXEC
構文を使用することです。これにより、一時テーブルまたはテーブル変数に挿入し、そこから必要なデータを選択することができます。
データを手動で検証するためにこれを行っている場合は、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();
手っ取り早い方法は、新しいパラメータ'@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
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;
それでおしまい!
質問で述べたように、ストアドプロシージャを実行する前に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
これを試して
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
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
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)
あなたが一度だけこれをする必要があるならばする最も簡単な方法:
インポート/エクスポートウィザードでExcelにエクスポートしてから、このExcelをテーブルにインポートします。