Có cách nào để tạo tập lệnh tạo bảng trong TSQL không?


22

Có cách nào để tạo tập lệnh tạo từ một bảng hiện có hoàn toàn trong T-SQL (nghĩa là không sử dụng SMO, vì T-SQL không có quyền truy cập vào SMO). Giả sử một thủ tục được lưu trữ nhận tên bảng và trả về một chuỗi chứa tập lệnh tạo cho bảng đã cho?

Bây giờ hãy để tôi mô tả tình huống tôi đang gặp phải, vì có thể có một cách khác để tiếp cận vấn đề này. Tôi có một ví dụ với vài chục cơ sở dữ liệu. Các cơ sở dữ liệu này đều có cùng một lược đồ, tất cả các bảng, chỉ mục giống nhau. Chúng được tạo ra như là một phần của cài đặt phần mềm của bên thứ ba. Tôi cần có cách làm việc với họ để tôi có thể tổng hợp dữ liệu từ họ theo cách đặc biệt. Những người tốt bụng tại dba.se đã giúp tôi ở đây Làm thế nào để tạo một kích hoạt trong cơ sở dữ liệu khác nhau?

Hiện tại tôi cần tìm một cách để chọn từ một bảng trên tất cả các cơ sở dữ liệu. Tôi đã ghi lại tất cả các tên cơ sở dữ liệu vào một bảng được gọi Databaseesvà tôi đã viết đoạn mã sau để thực thi một câu lệnh chọn trên tất cả chúng:

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

Tuy nhiên, đoạn script trên không thành công với thông báo sau:

Giá trị rõ ràng cho cột danh tính trong bảng '#tmp' chỉ có thể được chỉ định khi danh sách cột được sử dụng và IDENTITY_INSERT được BẬT.

Thêm điều này:

SET IDENTITY_INSERT #tmp ON

vì không giúp được gì, vì tôi không thể chỉ định danh sách cột và giữ nó chung chung.

Trong SQL, không có cách nào để tắt danh tính trên một bảng đã cho. Bạn chỉ có thể thả một cột và thêm một cột, điều này rõ ràng sẽ thay đổi thứ tự cột. Và nếu thứ tự cột thay đổi, bạn, một lần nữa, cần chỉ định danh sách cột, sẽ khác nhau tùy thuộc vào bảng bạn truy vấn.

Vì vậy, tôi đã suy nghĩ nếu tôi có thể lấy đoạn mã tạo bảng trong mã T-SQL của mình, tôi có thể thao tác nó với các biểu thức thao tác chuỗi để xóa cột định danh và cũng thêm một cột cho tên Cơ sở dữ liệu vào tập kết quả.

Bất cứ ai cũng có thể nghĩ ra một cách tương đối dễ dàng để đạt được những gì tôi muốn?

Câu trả lời:


28

Quay trở lại năm 2007, tôi đã yêu cầu một cách dễ dàng để tạo CREATE TABLEtập lệnh thông qua T-SQL thay vì sử dụng UI hoặc SMO. Tôi đã bị từ chối cuối cùng .

Tuy nhiên, SQL Server 2012 làm cho điều này rất dễ dàng. Hãy giả vờ rằng chúng ta có một bảng có cùng lược đồ trên nhiều cơ sở dữ liệu, ví dụ dbo.whatcha:

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

Kịch bản sau đây sử dụng sys.dm_exec_describe_first_results_setchức năng quản lý động mới để truy xuất các loại dữ liệu phù hợp cho từng cột (và bỏ qua thuộc IDENTITYtính). Nó xây dựng bảng #tmp mà bạn cần, chèn từ mỗi cơ sở dữ liệu trong danh sách của bạn và sau đó chọn từ #tmp, tất cả trong một lô SQL động duy nhất và không sử dụng WHILEvòng lặp (điều đó không làm cho nó tốt hơn, chỉ đơn giản hơn nhìn vào và cho phép bạn bỏ qua Database_Ref_Nohoàn toàn :-)).

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;

Kết quả PRINTđầu ra:

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;

Khi bạn tự tin rằng nó đang làm những gì bạn mong đợi, chỉ cần bỏ qua EXEC.

(Điều này tin tưởng bạn rằng lược đồ là như nhau; nó không xác nhận rằng một hoặc nhiều bảng đã bị thay đổi và kết quả có thể thất bại.)


Tại sao điều đó không tạo ra thông số kỹ thuật nhận dạng?
FindOutIslamNow

1
@Kilanny Bạn đã đọc toàn bộ câu trả lời, giống như phần tôi nói về lý do tại sao chúng ta bỏ qua tài sản nhận dạng?
Aaron Bertrand

Tôi cần định nghĩa bảng FULL (bao gồm danh tính, chỉ mục, ràng buộc, ..). Rất may, tôi đã tìm thấy tập lệnh tuyệt vời này Stormrage.com/QueryStuff/sp_GetDDLa_Latest.txt Dù sao cũng cảm ơn bạn
FindOutIslamNow

@Kilanny Tuyệt vời. Để rõ ràng, yêu cầu của bạn không phù hợp với yêu cầu trong câu hỏi này. Họ cần một bản sao của bảng mà không có danh tính vì họ đang sử dụng nó để sao chép dữ liệu hiện có, không tạo ra các hàng mới.
Aaron Bertrand

Aaron, đây là một giải pháp tao nhã trong tình trạng khó khăn khi bạn không cần chìa khóa, v.v ...
GWR

5

Không thể int T-SQL để tạo tập lệnh tạo đầy đủ của bảng. Ít nhất là không có cách xây dựng. bạn luôn có thể viết "trình tạo" của riêng bạn thông qua thông tin sys.columns.

Nhưng trong trường hợp của bạn, bạn không cần phải có được kịch bản tạo đầy đủ. Tất cả bạn cần là ngăn chặn việc SELECT INTOsao chép tài sản nhận dạng. Cách dễ nhất để làm điều đó là thêm một phép tính vào cột đó. Vì vậy, thay vì

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

bạn cần viết

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

Để tạo câu lệnh này, bạn có thể sử dụng lại sys.columns như trong SQL Fiddle này

Thiết lập lược đồ MS SQL Server 2008 :

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

Hai cột chúng ta cần là nameis_identity: Truy vấn 1 :

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

Kết quả :

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

Với điều đó, chúng ta có thể sử dụng một CASEcâu lệnh để tạo từng cột cho danh sách cột:

Truy vấn 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');

Kết quả :

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

Với một mẹo nhỏ XML, chúng ta có thể ghép tất cả những thứ này lại với nhau để có được danh sách cột đầy đủ:

Truy vấn 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,'')

Kết quả :

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

Xin lưu ý rằng bạn không thể tạo bảng #temp bằng SQL động và sử dụng nó bên ngoài câu lệnh đó vì bảng #temp vượt quá phạm vi khi câu lệnh sql động của bạn kết thúc. Vì vậy, bạn phải ép tất cả mã của mình vào cùng một chuỗi SQL động hoặc sử dụng một bảng thực. Nếu bạn cần có thể thực thi nhiều tập lệnh / thủ tục này cùng một lúc, bạn cần cho chúng tôi một tên bảng ngẫu nhiên, nếu không chúng sẽ bước lên nhau. Một cái gì đó như QUOTENAME(N'temp_'+CAST(NEWID() AS NVARCHAR(40))nên làm cho một tên đủ tốt.


Thay vì sao chép tất cả dữ liệu xung quanh, bạn cũng có thể sử dụng một kỹ thuật tương tự để chỉ tự động tạo chế độ xem cho mỗi bảng kết hợp tất cả các hóa thân của bảng đó trên tất cả các cơ sở dữ liệu. Tuy nhiên, tùy thuộc vào kích thước bảng có thể nhanh hơn hoặc chậm hơn, vì vậy bạn nên kiểm tra nó. Nếu bạn đi theo con đường này, tôi sẽ đưa những quan điểm đó vào một cơ sở dữ liệu riêng biệt.


1
Bạn có thể tạo bảng #temp và sau đó tham chiếu nó từ SQL động. Chỉ khi bạn tạo nó trong phạm vi đó thì nó không hiển thị sau khi SQL động đã thực thi.
Aaron Bertrand

3

Có một kịch bản tốt để đạt được điều này trong bài viết SQLServerCentral:

Phiên bản mới nhất hiện tại của tập lệnh cũng có sẵn dưới dạng văn bản tại đây (Stormrage.com).

Tôi ước có một cách để bao gồm tất cả các kịch bản ở đây, bởi vì nó hoạt động với tôi. Kịch bản chỉ quá dài để dán ở đây.

Thông báo bản quyền:

--#################################################################################################
-- 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

Bạn có thể tạo một bản thô CREATE TABLEbằng cách sử dụng SQL động từ dữ liệu trong INFORMATION_SCHEMA.COLUMNS.

Nếu bạn cần thêm các ràng buộc, v.v. bạn sẽ cần thêm thông tin từ một số INFORMATION_SCHEMAquan điểm khác .

Tài liệu Microsoft

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.