有没有一种方法可以在TSQL中生成表创建脚本?


22

有没有一种方法可以完全通过T-SQL从现有表中生成创建脚本(即不使用SMO,因为T-SQL无法访问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

但是,上面的脚本失败,并显示以下消息:

仅当使用列列表且IDENTITY_INSERT为ON时,才能为表'#tmp'中的identity列指定一个显式值。

添加:

SET IDENTITY_INSERT #tmp ON

这无济于事,因为我无法指定列列表并使它保持通用。

在SQL中,无法关闭给定表上的标识。您只能删除一列并添加一列,这显然会改变列的顺序。而且,如果列顺序更改,则需要再次指定列列表,这取决于查询的表。

因此,我在考虑是否可以在T-SQL代码中获得创建表脚本,我可以使用字符串操作表达式对其进行操作以删除标识列,并在结果集中添加一列数据库名称

谁能想到一个相对简单的方法来实现我想要的?

Answers:


28

早在2007年,我就要求一种CREATE TABLE通过T-SQL而不是使用UI或SMO 生成脚本的简单方法。我被拒绝了

但是,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

(这使您相信该模式是相同的;它不验证此后已更改了一个或多个表,因此可能会失败。)


为什么不生成身份规范?
FindOutIslamNow16年

1
@Kilanny您是否阅读了完整的答案,就像我谈论为什么我们忽略身份属性的那一部分一样?
亚伦·伯特兰

我需要完整的表定义(包括身份,索引,约束等)。值得庆幸的是,我发现了这个很棒的脚本stormrage.com/SQLStuff/sp_GetDDLa_Latest.txt 无论如何,谢谢您
FindOutIslamNow

@基兰妮太棒了。需要明确的是,您的要求与该问题的要求不匹配。他们需要一个没有身份的表副本,因为他们使用它来复制现有数据,而不是生成新行。
亚伦·伯特兰

亚伦,这是一个紧要关头,不需要钥匙等的优雅解决方案……
GWR

5

int T-SQL不可能生成表的完整创建脚本。至少没有构建方式。您总是可以编写自己的“生成器”浏览信息sys.columns

但是在您的情况下,您无需获取完整的创建脚本。您需要做的就是防止SELECT INTO复制身份属性。最简单的方法是将计算添加到该列。所以代替

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
);

我们需要的两列是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文档

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.