私はSQL Server 2008でストアドプロシージャを使用していますが、INSERT INTO
データを処理するために事前定義された一時テーブル。私が一時テーブルを定義する方法を理解する方法を除いて、それは大丈夫です、私がその定義をリストしてコードを読む以外のストアドプロシージャを書いたものではない場合は?
たとえば、「EXEC sp_stored_procedure」の一時テーブルはどのようになりますか?これは単純なストアドプロシージャであり、おそらくデータ型を推測できますが、プロシージャの実行から返される列の型と長さを読み取る方法があるに違いないようです。
したがって、tempdbにストアドプロシージャがあるとします。
USE tempdb;
GO
CREATE PROCEDURE dbo.my_procedure
AS
BEGIN
SET NOCOUNT ON;
SELECT foo = 1, bar = 'tooth';
END
GO
ストアドプロシージャが出力するメタデータを決定するには、かなり複雑な方法があります。プロシージャには単一の結果セットのみを出力でき、正確に判断できない場合はデータ型について最良の推測が行われるなど、いくつかの注意事項があります。 OPENQUERY
を使用し、'DATA ACCESS'
プロパティをtrueに設定したループバックリンクサーバーを使用する必要があります。 sys.serversをチェックして、有効なサーバーが既にあるかどうかを確認できますが、手動でloopback
と呼ばれるサーバーを作成してみましょう。
EXEC master..sp_addlinkedserver
@server = 'loopback',
@srvproduct = '',
@provider = 'SQLNCLI',
@datasrc = @@SERVERNAME;
EXEC master..sp_serveroption
@server = 'loopback',
@optname = 'DATA ACCESS',
@optvalue = 'TRUE';
これをリンクサーバーとしてクエリできるようになったので、通常のSELECT
としてクエリの結果(ストアドプロシージャコールを含む)を使用できます。そのため、これを行うことができます(データベースプレフィックスが重要です、そうでない場合はエラー11529および2812が表示されます):
SELECT * FROM OPENQUERY(loopback, 'EXEC tempdb.dbo.my_procedure;');
SELECT *
を実行できる場合は、SELECT * INTO
も実行できます。
SELECT * INTO #tmp FROM OPENQUERY(loopback, 'EXEC tempdb.dbo.my_procedure;');
そして、その#tmpテーブルが存在すると、次のように言ってメタデータを決定できます(SQL Server 2005以降を想定)。
SELECT c.name, [type] = t.name, c.max_length, c.[precision], c.scale
FROM sys.columns AS c
INNER JOIN sys.types AS t
ON c.system_type_id = t.system_type_id
AND c.user_type_id = t.user_type_id
WHERE c.[object_id] = OBJECT_ID('tempdb..#tmp');
(SQL Server 2000を使用している場合は、syscolumnsで同様のことができますが、同等のクエリを検証するのに便利な2000インスタンスはありません。)
結果:
name type max_length precision scale
--------- ------- ---------- --------- -----
foo int 4 10 0
bar varchar 5 0 0
デナリでは、これははるかに簡単になります。繰り返しますが、最初の結果セットにはまだ制限がありますが、リンクサーバーをセットアップしてそれらすべてのフープをジャンプする必要はありません。あなたはただ言うことができます:
DECLARE @sql NVARCHAR(MAX) = N'EXEC tempdb.dbo.my_procedure;';
SELECT name, system_type_name
FROM sys.dm_exec_describe_first_result_set(@sql, NULL, 1);
結果:
name system_type_name
--------- ----------------
foo int
bar varchar(5)
デナリまでは、袖をまくり、自分でデータ型を把握する方が簡単だと思います。上記の手順を実行するのが退屈だからというだけでなく、エンジンが行うデータ型の推定は実行時間に基づいているため、エンジンよりもはるかに正確な(または少なくともより正確な)推定を行う可能性が高いためです可能な値のドメインに関する外部知識なしで出力。この要素はデナリでも同様ですので、新しいメタデータ検出機能がすべてであるという印象を受けないようにしてください。
ああ、その他のOPENQUERY
の潜在的な落とし穴については、Erland Sommarskogの記事をご覧ください
より洗練されていない方法(場合によっては十分かもしれません):元のSPを編集し、最後のSELECTの後、FROM句の前にINSERT INTO tmpTableを追加してSP結果をtmpTableに保存します。
実際のデータを取得するために、できれば意味のあるパラメーターを使用して、変更したSPを実行します。プロシージャの元のコードを復元します。
これで、SQL Server Management StudioからtmpTableのスクリプトを取得するか、sys.columnsをクエリしてフィールドの説明を取得できます。
SQL 2012には、これを支援する新しいSPがあるようです。
exec sp_describe_first_result_set N'PROC_NAME'
これが私が書いたコードです。アイデアは(他の誰かが述べたように)SPコードを取得し、修正して実行することです。しかし、私のコードは元のSPを変更しません。
最初のステップは、SPの定義を取得し、「作成」部分を取り除き、パラメーター宣言がある場合は「AS」を取り除きます。
Declare @SPName varchar(250)
Set nocount on
Declare @SQL Varchar(max), @SQLReverse Varchar(MAX), @StartPos int, @LastParameterName varchar(250) = '', @TableName varchar(36) = 'A' + REPLACE(CONVERT(varchar(36), NewID()), '-', '')
Select * INTO #Temp from INFORMATION_SCHEMA.PARAMETERS where SPECIFIC_NAME = 'ADMIN_Sync_CompareDataForSync'
if @@ROWCOUNT > 0
BEGIN
Select @SQL = REPLACE(ROUTINE_DEFINITION, 'CREATE PROCEDURE [' + ROUTINE_SCHEMA + '].[' + ROUTINE_NAME + ']', 'Declare')
from INFORMATION_SCHEMA.ROUTINES
where ROUTINE_NAME = @SPName
Select @LastParameterName = PARAMETER_NAME + ' ' + DATA_TYPE +
CASE WHEN CHARACTER_MAXIMUM_LENGTH is not null THEN '(' +
CASE WHEN CHARACTER_MAXIMUM_LENGTH = -1 THEN 'MAX' ELSE CONVERT(varchar,CHARACTER_MAXIMUM_LENGTH) END + ')' ELSE '' END
from #Temp
WHERE ORDINAL_POSITION =
(Select MAX(ORDINAL_POSITION)
From #Temp)
Select @StartPos = CHARINDEX(@LastParameterName, REPLACE(@SQL, ' ', ' '), 1) + LEN(@LastParameterName)
END
else
Select @SQL = REPLACE(ROUTINE_DEFINITION, 'CREATE PROCEDURE [' + ROUTINE_SCHEMA + '].[' + ROUTINE_NAME + ']', '') from INFORMATION_SCHEMA.ROUTINES where ROUTINE_NAME = @SPName
DROP TABLE #Temp
Select @StartPos = CHARINDEX('AS', UPPER(@SQL), @StartPos)
Select @SQL = STUFF(@SQL, @StartPos, 2, '')
(一意の識別子に基づいた新しいテーブル名の作成に注意してください)これで、結果セットを返す選択を行うコードであると仮定して、コード内の最後の「From」ワードを見つけます。
Select @SQLReverse = REVERSE(@SQL)
Select @StartPos = CHARINDEX('MORF', UPPER(@SQLReverse), 1)
コードを変更して、結果セットをテーブル(uniqueidentifierに基づくテーブル)に選択します
Select @StartPos = LEN(@SQL) - @StartPos - 2
Select @SQL = STUFF(@SQL, @StartPos, 5, ' INTO ' + @TableName + ' FROM ')
EXEC (@SQL)
結果セットはテーブルになりました。テーブルが空かどうかは関係ありません!
テーブルの構造を取得できます
Select * from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @TableName
これで魔法をかけることができます
その一意のテーブルを削除することを忘れないでください
Select @SQL = 'drop table ' + @TableName
Exec (@SQL)
お役に立てれば!