มีวิธีสร้างตารางสร้างสคริปต์ใน TSQL หรือไม่?


22

มีวิธีสร้างสคริปต์สร้างจากตารางที่มีอยู่ใน T-SQL (โดยไม่ใช้ SMO เนื่องจาก T-SQL ไม่สามารถเข้าถึง SMO ได้) สมมติว่าเป็นกระบวนงานที่เก็บไว้ที่รับชื่อตารางและส่งคืนสตริงที่มีสคริปต์สร้างสำหรับตารางที่กำหนดหรือไม่

ตอนนี้ให้ฉันอธิบายสถานการณ์ที่ฉันกำลังเผชิญเนื่องจากอาจมีวิธีอื่นในการเข้าถึงสิ่งนี้ ฉันมีอินสแตนซ์ที่มีฐานข้อมูลหลายโหล ฐานข้อมูลเหล่านี้ทั้งหมดมีสคีมาเหมือนกันทั้งหมดตารางเดียวกันดัชนีและอื่น ๆ พวกเขาถูกสร้างขึ้นเป็นส่วนหนึ่งของการติดตั้งซอฟต์แวร์บุคคลที่สาม ฉันต้องมีวิธีในการทำงานกับพวกเขาเพื่อให้ฉันสามารถรวบรวมข้อมูลจากพวกเขาในลักษณะเฉพาะกิจ ผู้คนที่ดีที่ dba.se ได้ช่วยฉันที่นี่แล้วจะสร้างทริกเกอร์ในฐานข้อมูลอื่นได้อย่างไร

ขณะนี้ฉันต้องการค้นหาวิธีการเลือกจากตารางในฐานข้อมูลทั้งหมด ฉันได้บันทึกชื่อฐานข้อมูลทั้งหมดลงในตารางที่เรียกDatabaseesและฉันเขียนสคริปต์ต่อไปนี้เพื่อดำเนินการคำสั่งที่เลือกในพวกเขาทั้งหมด:

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' สามารถระบุได้เมื่อมีการใช้รายการคอลัมน์และ IDENTITY_INSERT เปิดอยู่

เพิ่มสิ่งนี้:

SET IDENTITY_INSERT #tmp ON

ไม่ช่วยเนื่องจากฉันไม่สามารถระบุรายการคอลัมน์และเก็บไว้ทั่วไป

ใน SQL ไม่มีวิธีการเปลี่ยนข้อมูลประจำตัวในตารางที่กำหนด คุณสามารถวางคอลัมน์และเพิ่มคอลัมน์เท่านั้นซึ่งเปลี่ยนลำดับคอลัมน์อย่างชัดเจน และหากลำดับคอลัมน์เปลี่ยนไปคุณต้องระบุรายการคอลัมน์อีกครั้งซึ่งจะแตกต่างกันไปขึ้นอยู่กับตารางที่คุณสืบค้น

ดังนั้นฉันคิดว่าถ้าฉันสามารถสร้างตาราง scrip ในรหัส T-SQL ของฉันฉันสามารถจัดการกับนิพจน์การจัดการสตริงเพื่อลบคอลัมน์ข้อมูลประจำตัวและเพิ่มคอลัมน์สำหรับชื่อฐานข้อมูลไปยังชุดผลลัพธ์

ทุกคนสามารถคิดวิธีที่ง่ายในการบรรลุสิ่งที่ฉันต้องการได้หรือไม่

คำตอบ:


28

ย้อนกลับไปในปี 2550 ฉันขอวิธีง่ายๆในการสร้าง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เมื่อคุณมีความมั่นใจก็ทำสิ่งที่คุณคาดหวังเพียง uncomment

(สิ่งนี้เชื่อใจคุณว่าสคีมาเหมือนกันมันไม่ได้ตรวจสอบว่าตั้งแต่หนึ่งตารางขึ้นไปนับตั้งแต่มีการเปลี่ยนแปลงและอาจล้มเหลวเป็นผล)


เหตุใดจึงไม่สร้างรายละเอียดตัวตน?
FindOutIslamNow

1
@Kilanny คุณอ่านคำตอบทั้งหมดเช่นเดียวกับส่วนที่ฉันพูดถึงว่าทำไมเราถึงเพิกเฉยต่อทรัพย์สินที่เป็นเอกลักษณ์?
Aaron Bertrand

ฉันต้องการคำจำกัดความของตารางเต็ม (รวมถึงตัวตนดัชนีข้อ จำกัด .. ) โชคดีที่ฉันพบสคริปต์ที่ยอดเยี่ยมนี้stormrage.com/SQLStuff/sp_GetDDLa_Latest.txt ขอบคุณอยู่แล้ว
FindOutIslamNow

@Kilanny ที่ดี เพื่อให้ชัดเจนความต้องการของคุณไม่ตรงกับความต้องการในคำถามนี้ พวกเขาต้องการสำเนาของตารางโดยไม่มีตัวตนเพราะพวกเขาใช้มันเพื่อคัดลอกข้อมูลที่มีอยู่ไม่ได้สร้างแถวใหม่
Aaron Bertrand

แอรอนนี่เป็นคำตอบที่สง่างามในไม่กี่ชั่วโมงโดยที่คุณไม่จำเป็นต้องใช้กุญแจ ฯลฯ ...
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.column อีกครั้งเหมือนในSQL Fiddle นี้

การติดตั้ง Schema MS SQL Server 2008 :

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

คอลัมน์ที่สองเราต้องมีnameและis_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] |

โปรดทราบว่าคุณไม่สามารถสร้างตาราง #temp โดยใช้ไดนามิก SQL และใช้งานนอกคำสั่งนั้นเนื่องจากตาราง #temp ไม่อยู่ในขอบเขตเมื่อคำสั่งไดนามิก sql ของคุณเสร็จสิ้น ดังนั้นคุณต้องบีบรหัสของคุณทั้งหมดลงในสตริง 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.