TSQLでテーブル作成スクリプトを生成する方法はありますか?


22

T-SQLのみで既存のテーブルから作成スクリプトを生成する方法はありますか(T-SQLはSMOにアクセスできないため、SMOを使用しません)。テーブル名を受け取り、指定されたテーブルの作成スクリプトを含む文字列を返すストアドプロシージャがあるとしますか?

これにアプローチする別の方法があるかもしれないので、今私が直面している状況を説明させてください。数十のデータベースを持つインスタンスがあります。これらのデータベースはすべて同じスキーマ、すべて同じテーブル、インデックスなどを持っています。これらは、サードパーティのソフトウェアインストールの一部として作成されました。アドホックな方法でデータを集約できるように、それらを操作する方法が必要です。dba.seの素敵な人々がここですでに私を助けてくれました別のデータベースにトリガーを作成する方法は?

現在、すべてのデータベースのテーブルから選択を行う方法を見つける必要があります。という名前のテーブルにすべてのデータベース名を記録し、Databaseesそれらすべてに対してselectステートメントを実行する次のスクリプトを作成しました。

IF OBJECT_ID('tempdb..#tmp') IS NOT NULL
DROP TABLE #tmp

select * into #tmp from Database1.dbo.Table1 where 1=0
DECLARE @statement nvarchar(max) = 
  N'insert into #tmp select * from Table1 where Column1=0 and Cloumn2 =1'

DECLARE @LastDatabaseID INT
SET @LastDatabaseID = 0

DECLARE @DatabaseNameToHandle varchar(60)
DECLARE @DatabaseIDToHandle int

SELECT TOP 1 @DatabaseNameToHandle = Name,
@DatabaseIDToHandle = Database_Ref_No
FROM Databasees
WHERE Database_Ref_No > @LastDatabaseID
ORDER BY Database_Ref_No

WHILE @DatabaseIDToHandle IS NOT NULL
BEGIN

  DECLARE @sql NVARCHAR(MAX) = QUOTENAME(@DatabaseNameToHandle) + '.dbo.sp_executesql'
  EXEC @sql @statement

  SET @LastDatabaseID = @DatabaseIDToHandle
  SET @DatabaseIDToHandle = NULL

  SELECT TOP 1 @DatabaseNameToHandle = Name,
  @DatabaseIDToHandle = Database_Ref_No
  FROM Databasees
  WHERE Database_Ref_No > @LastDatabaseID
  ORDER BY Database_Ref_No
END

select * from #tmp
DROP TABLE #tmp

ただし、上記のスクリプトは失敗し、次のメッセージが表示されます。

テーブル '#tmp'のID列の明示的な値は、列リストが使用され、IDENTITY_INSERTがONの場合にのみ指定できます。

これを追加する:

SET IDENTITY_INSERT #tmp ON

列リストを指定して汎用的に保つことはできないため、助けにはなりません。

SQLでは、特定のテーブルのIDをオフに切り替える方法はありません。列をドロップして列を追加するだけで、列の順序が明らかに変更されます。また、列の順序が変更された場合は、列リストを指定する必要があります。これは、クエリするテーブルによって異なります。

したがって、T-SQLコードでcreate table scripを取得できたら、文字列操作式で操作してID列を削除し、データベース名の列を結果セットに追加できると考えていました。

誰でも私が望むものを達成する比較的簡単な方法を考えることができますか?

回答:


28

2007年に、CREATE TABLEUIまたはSMOを使用するのではなく、T-SQLを介してスクリプトを生成する簡単な方法を求めました。私はすぐに拒否されました

ただし、SQL Server 2012はこれを非常に簡単にします。複数のデータベース間で同じスキーマを持つテーブルがあるとします、例えばdbo.whatcha

CREATE TABLE dbo.whatcha
(
  id INT IDENTITY(1,1), 
  x VARCHAR(MAX), 
  b DECIMAL(10,2), 
  y SYSNAME
);

次のスクリプトは、新しいsys.dm_exec_describe_first_results_set動的管理関数を使用して、各列の適切なデータ型を取得します(IDENTITYプロパティを無視します)。必要な#tmpテーブルを構築し、リスト内の各データベースから挿入し、WHILEループを使用せずに単一の動的SQLバッチ内ですべてを#tmpから選択します(ループは改善されず、単純になります)を見て、Database_Ref_No完全に無視することができます:-))。

SET NOCOUNT ON;

DECLARE @sql NVARCHAR(MAX), @cols NVARCHAR(MAX) = N'';

SELECT @cols += N',' + name + ' ' + system_type_name
  FROM sys.dm_exec_describe_first_result_set(N'SELECT * FROM dbo.whatcha', NULL, 1);

SET @cols = STUFF(@cols, 1, 1, N'');

SET @sql = N'CREATE TABLE #tmp(' + @cols + ');'

DECLARE @dbs TABLE(db SYSNAME);

INSERT @dbs VALUES(N'db1'),(N'db2');
  -- SELECT whatever FROM dbo.databases

SELECT @sql += N'
  INSERT #tmp SELECT ' + @cols + ' FROM ' + QUOTENAME(db) + '.dbo.tablename;'
  FROM @dbs;

SET @sql += N'
  SELECT ' + @cols + ' FROM #tmp;';

PRINT @sql;
-- EXEC sp_executesql @sql;

結果のPRINT出力:

CREATE TABLE #tmp(id int,x varchar(max),b decimal(10,2),y nvarchar(128));
  INSERT #tmp SELECT id,x,b,y FROM [db1].dbo.tablename;
  INSERT #tmp SELECT id,x,b,y FROM [db2].dbo.tablename;
  SELECT id,x,b,y FROM #tmp;

期待どおりに動作していると確信したら、コメントを外しますEXEC

(これにより、スキーマが同じであることが信頼されます。1つ以上のテーブルがその後変更されたことは検証されず、結果として失敗する可能性があります。)


なぜそれがアイデンティティ仕様を生成しないのですか?
FindOutIslamNow

1
@Kilannyアイデンティティのプロパティを無視している理由について話している部分のように、答え全体を読みましたか?
アーロンバートランド

完全なテーブル定義(ID、インデックス、制約などを含む)が必要です。ありがたいことに、この素​​晴らしいスクリプトを見つけ ました
stormrage.com/SQLStuff/sp_GetDDLa_Latest.txt

@Kilanny素晴らしい。明確にするために、要件はこの質問の要件と一致しません。新しい行を生成するのではなく、既存のデータをコピーするためにそれを使用していたため、アイデンティティのないテーブルのコピーが必要でした。
アーロンバートランド

アーロン、これはキーなどを必要としないピンチでのエレガントなソリューションです
GWR

5

T-SQLでテーブルの完全な作成スクリプトを生成することはできません。少なくともビルドの方法はありません。常に情報を調べる独自の「ジェネレータ」を書くことができますsys.columns

しかし、あなたの場合、完全な作成スクリプトを取得する必要はありません。必要なのはSELECT INTO、IDプロパティをコピーしないようにすることだけです。それを行う最も簡単な方法は、その列に計算を追加することです。の代わりに

select * into #tmp from Database1.dbo.Table1 where 1=0

あなたは書く必要があります

select id*0 as id, other, column, names into #tmp from Database1.dbo.Table1 where 1=0

このステートメントを生成するには、このSQL Fiddleのようにsys.columnsを再度使用できます。

MS SQL Server 2008スキーマのセットアップ

CREATE TABLE dbo.testtbl(
    id INT IDENTITY(1,1),
    other NVARCHAR(MAX),
    [column] INT,
    [name] INT
);

私たちが必要とする2つの列があるnameis_identityクエリ1

SELECT name,is_identity
  FROM sys.columns
 WHERE object_id = OBJECT_ID('dbo.testtbl');

結果

|   NAME | IS_IDENTITY |
|--------|-------------|
|     id |           1 |
|  other |           0 |
| column |           0 |
|   name |           0 |

これにより、CASEステートメントを使用して列リストの各列を生成できます。

クエリ2

SELECT ','+ 
    CASE is_identity
    WHEN 1 THEN QUOTENAME(name)+'*0 AS '+QUOTENAME(name)
    ELSE QUOTENAME(name)
    END
  FROM sys.columns
 WHERE object_id = OBJECT_ID('dbo.testtbl');

結果

|        COLUMN_0 |
|-----------------|
| ,[id]*0 AS [id] |
|        ,[other] |
|       ,[column] |
|         ,[name] |

少しのXMLトリックを使用して、これらすべてを連結して完全な列リストを取得できます。

クエリ3

SELECT STUFF((
  SELECT ','+ 
      CASE is_identity
      WHEN 1 THEN QUOTENAME(name)+'*0 AS '+QUOTENAME(name)
      ELSE QUOTENAME(name)
      END
    FROM sys.columns
   WHERE object_id = OBJECT_ID('dbo.testtbl')
   ORDER BY column_id
     FOR XML PATH(''),TYPE
  ).value('.','NVARCHAR(MAX)'),1,1,'')

結果

|                               COLUMN_0 |
|----------------------------------------|
| [id]*0 AS [id],[other],[column],[name] |

動的SQLステートメントを終了すると、#tempテーブルがスコープ外になるため、動的SQLを使用して#tempテーブルを作成し、そのステートメントの外部で使用することはできないことに注意してください。したがって、すべてのコードを同じ動的SQL文字列に圧縮するか、実際のテーブルを使用する必要があります。これらのスクリプト/プロシージャを複数同時に実行できるようにする必要がある場合は、ランダムなテーブル名を指定する必要があります。そうしないと、互いにステップ実行します。このようQUOTENAME(N'temp_'+CAST(NEWID() AS NVARCHAR(40))な名前は、十分な名前にする必要があります。


すべてのデータをコピーする代わりに、同様の手法を使用して、すべてのデータベースにわたってそのテーブルのすべてのインカネーションを結合する各テーブルのビューを自動生成することもできます。ただし、テーブルのサイズに応じて、高速または低速になる可能性があるため、テストする必要があります。このルートに進むと、それらのビューを別のデータベースに入れます。


1
#tempテーブルを作成して、動的SQLからそれを正常に参照できます。そのスコープで作成した場合のみ、動的SQLの実行後に表示されなくなります。
アーロンバートランド

3

SQLServerCentralの記事には、これを実現する優れたスクリプトがあります。

スクリプトの現在の最新バージョンはテキストとしても利用可能である、ここで(stormrage.com)。

ここにすべてのスクリプトを含める方法があればいいのですが、それは私にとってはうまくいくからです。スクリプトは長すぎてここに貼り付けられません。

著作権表示:

--#################################################################################################
-- copyright 2004-2013 by Lowell Izaguirre scripts*at*stormrage.com all rights reserved.
-- http://www.stormrage.com/SQLStuff/sp_GetDDL_Latest.txt
--Purpose: Script Any Table, Temp Table or Object
--
-- see the thread here for lots of details: http://www.sqlservercentral.com/Forums/Topic751783-566-7.aspx

-- You can use this however you like...this script is not rocket science, but it took a bit of work to create.
-- the only thing that I ask
-- is that if you adapt my procedure or make it better, to simply send me a copy of it,
-- so I can learn from the things you've enhanced.The feedback you give will be what makes
-- it worthwhile to me, and will be fed back to the SQL community.
-- add this to your toolbox of helpful scripts.
--#################################################################################################

-1

CREATE TABLEのデータから動的SQLを使用してラフを生成できますINFORMATION_SCHEMA.COLUMNS

制約などを追加する必要がある場合は、他のいくつかのINFORMATION_SCHEMAビューから情報を追加する必要があります。

Microsoftのドキュメント

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.