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 코드에서 테이블 스크립 생성을 얻을 수 있는지 생각하고 문자열 조작 표현식으로 조작하여 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 테이블을 빌드하고 목록의 각 데이터베이스에서 삽입 한 다음 단일 동적 SQL 배치 내에서 WHILE루프 를 사용하지 않고 #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.

(이것은 스키마가 동일하다는 것을 신뢰합니다. 이후 하나 이상의 테이블이 변경되어 결과적으로 실패 할 수 있음을 검증하지 않습니다.)


이것이 왜 아이덴티티 스펙을 생성하지 않습니까?
FindOutIslamNow

1
@Kilanny ID 속성을 무시하는 이유에 대해 이야기하는 부분과 같이 전체 답변을 읽었습니까?
Aaron Bertrand

FULL 테이블 정의 (ID, 인덱스, 제약 조건 포함)가 필요합니다. 고맙게도이 위대한 스크립트를 찾았습니다 stormrage.com/SQLStuff/sp_GetDDLa_Latest.txt 감사합니다
FindOutIslamNow

@ 킬 라니 그레이트. 분명히하기 위해 귀하의 요구 사항 이이 질문의 요구 사항과 일치하지 않습니다. 새 행을 생성하지 않고 기존 데이터를 복사하는 데 사용했기 때문에 ID가 없는 테이블의 사본이 필요했습니다 .
Aaron Bertrand

아론, 이것은 열쇠 등을 필요로하지 않는 핀치의 우아한 솔루션입니다.
GWR

5

int T-SQL에서 테이블의 전체 작성 스크립트를 생성 할 수 없습니다. 최소한 방해가되지 않습니다. 당신은 항상 정보를 통해 자신의 "발전기"를 작성할 수 있습니다 sys.columns.

그러나 귀하의 경우 전체 작성 스크립트를 얻을 필요가 없습니다. SELECT INTOID 속성을 복사 하지 못하도록하면 됩니다. 가장 쉬운 방법은 해당 열에 계산을 추가하는 것입니다. 그래서 대신

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

필요한 두 열은 다음 nameis_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))이름은 충분히 좋은 이름을 만들어야합니다.


모든 데이터를 복사하는 대신 유사한 기술을 사용하여 모든 데이터베이스에서 해당 테이블의 모든 화신을 통합하는 각 테이블에 대한 뷰를 자동 생성 할 수 있습니다. 그러나 테이블 크기에 따라 빠르거나 느릴 수 있으므로 테스트해야합니다. 이 경로를 사용하면 해당 뷰를 별도의 데이터베이스에 넣을 것입니다.


1
#temp 테이블을 만든 다음 동적 SQL에서 잘 참조 할 수 있습니다. 해당 범위에서 동적 SQL을 실행 한 후 표시되지 않는 범위에서 작성하는 경우에만 해당됩니다.
Aaron Bertrand

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.