web-dev-qa-db-ja.com

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

純粋に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コードでテーブル作成スクリプトを取得できたら、文字列操作式で操作してID列を削除し、データベース名の列を追加できるかと考えていました。 結果セットに。

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

22
Andrew Savinykh

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

ただし、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テーブルを作成し、リスト内の各データベースから挿入し、#tmpから選択します。すべて単一の動的SQLバッチ内で、WHILEループを使用しません(これにより、見た目が簡単で、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つ以上のテーブルが変更されていることが検証されないため、結果として失敗する可能性があります。)

28
Aaron Bertrand

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
_

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

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

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

必要な2つの列はnameおよび_is_identity_:Query 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))のようなものは、十分な名前になります。


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

5
Sebastian Meine

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

スクリプトの最新バージョンは、テキスト here (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.
--#################################################################################################
3