TSQL'de tablo oluşturma komut dosyası oluşturmanın bir yolu var mı?


22

Mevcut bir tablodan yalnızca T-SQL'de bir oluşturma komut dosyası oluşturmanın bir yolu var mı (T-SQL'in SMO'ya erişimi olmadığından SMO kullanmadan). Diyelim ki bir tablo adı alan ve verilen tablo için create betiğini içeren bir dize döndüren saklı bir prosedür?

Şimdi, karşılaştığım durumu açıklamama izin verin, çünkü buna yaklaşmanın farklı bir yolu olabilir. Birkaç düzine veritabanına sahip bir örneğim var. Bu veritabanlarının tümü aynı şemaya, aynı tablolara, indekslere vb. Sahiptir. Üçüncü parti bir yazılım kurulumunun parçası olarak yaratılmışlar. Onlarla çalışmanın bir yolunu bulmalıyım, böylece verileri onlardan geçici bir şekilde toplayabiliyorum. Dba.se'deki güzel insanlar burada bana çoktan yardım ettiler Farklı bir veritabanında tetikleyici nasıl oluşturulur?

Şu anda tüm veritabanlarındaki bir tablodan seçim yapmanın bir yolunu bulmam gerekiyor. Tüm veritabanı adlarını bir tabloya kaydettim ve hepsine Databaseesbir select cümlesi uygulamak için aşağıdaki betiği yazdım:

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

Ancak, yukarıdaki komut dosyası aşağıdaki iletiyle başarısız oluyor:

'#Tmp' tablosundaki kimlik sütunu için açık bir değer yalnızca bir sütun listesi kullanıldığında ve IDENTITY_INSERT ON olduğunda belirlenebilir.

Bunu ekleyerek:

SET IDENTITY_INSERT #tmp ON

yardımcı olmuyor, çünkü sütun listesini belirleyemiyorum ve genel olarak tutamıyorum.

SQL'de kimliği belirli bir masaya kapatmanın bir yolu yoktur. Yalnızca bir sütunu bırakabilir ve bir sütun ekleyebilirsiniz; bu, açıkça sütun sırasını değiştirir. Ve sütun sırası değişirse, yine, sorguladığınız tabloya bağlı olarak farklı olacak olan sütun listesini belirtmeniz gerekir.

Bu nedenle, T-SQL kodumda tablo tablosu komut dosyasını alabilirsem, kimlik sütununu kaldırmak ve sonuç kümesine Veritabanı adı için bir sütun eklemek için onu dize işleme ifadeleriyle değiştirebilirim .

Herhangi biri istediğimi elde etmek için nispeten kolay bir yol düşünebilir mi?

Yanıtlar:


28

2007 yılında CREATE TABLE, UI veya SMO kullanmak yerine, T-SQL yoluyla bir komut dosyası oluşturmanın kolay bir yolunu istedim . Özetle reddedildi .

Ancak, SQL Server 2012 bunu çok kolaylaştırıyor. Birden fazla veritabanında aynı şemaya sahip bir tablomuz olduğunu farzedelim, örneğin dbo.whatcha:

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

Aşağıdaki komut dosyası, her bir sütun için uygun veri türlerini almak (ve özelliği yoksaymak ) için yeni sys.dm_exec_describe_first_results_setdinamik yönetim işlevini kullanır IDENTITY. İhtiyacınız olan #tmp tablosunu oluşturur, listenizdeki veritabanlarının her birinden ekler ve ardından tek bir dinamik SQL toplu işleminde ve bir WHILEdöngü kullanmadan #tmp arasından seçim yapar (bu işlemi daha iyi yapmaz, sadece daha basit hale getirir) bakın ve Database_Ref_Notamamen görmezden gelmenizi sağlar :-)).

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;

Sonuçta elde edilen PRINTçıktı:

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;

Emin olduğunuzda, beklediğiniz şeyi yapıyor, sadece rahatsız ediyor EXEC.

(Bu şemanın aynı olduğuna güveniyor; tablolardan birinin veya daha fazlasının değiştirildiğini doğrulamıyor ve sonuç olarak başarısız olabilir.)


Bu neden kimlik özelliklerini oluşturmuyor?
FindOutIslamNow

1
@Kilanny Kimlik özelliğini neden görmezden geldiğimiz hakkında konuştuğum bölüm gibi tüm cevabı okudunuz mu?
Aaron Bertrand

FULL tablo tanımına ihtiyacım var (kimlik, dizinler, kısıtlamalar, .. dahil). Neyse ki bu harika betiği buldum stormrage.com/SQLStuff/sp_GetDDLa_Latest.txt Yine de teşekkürler
FindOutIslamNow

@Kilanny Harika. Açık olmak gerekirse, gereksiniminiz bu sorudaki gereksinimle aynı değil. Masanın bir kopyasını gerekli olmadan onlar yeni satırlar üreten değil, mevcut verileri kopyalamak için kullanarak çünkü kimlik.
Aaron Bertrand

Aaron, bu anahtarlara ihtiyacın olmayan bir tutamdaki şık bir çözüm ...
GWR

5

İnt T-SQL bir tablonun tam bir yaratma komut dosyasını oluşturmak mümkün değildir. En azından bir yol yok. her zaman kendi "jeneratörünüzü" bilgiden geçerek yazabilirsiniz sys.columns.

Ancak sizin durumunuzda, tam yaratma betiğini almanız gerekmez. İhtiyacınız olan tek şey SELECT INTO, kimlik özelliğini kopyalamanın engellenmesidir . Bunu yapmanın en kolay yolu, bu sütuna bir hesaplama eklemektir. Yani yerine

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

yazman gerek

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

Bu ifadeyi oluşturmak için tekrar bu SQL Fiddle'daki gibi sys.columns kullanabilirsiniz.

MS SQL Server 2008 Şema Kurulumu :

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

İhtiyacımız olan iki sütun nameve is_identity: Sorgu 1 :

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

Sonuçlar :

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

Bununla CASEsütun listesi için her sütunu oluşturmak için bir ifade kullanabiliriz :

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

Sonuçlar :

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

Küçük bir XML hilesiyle, tam sütun listesini elde etmek için hepsini bir araya getirebiliriz:

Sorgu 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,'')

Sonuçlar :

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

Dinamik SQL kullanarak bir #temp tablosu oluşturamayacağınızı ve #temp tablosu dinamik sql ifadeniz bittiğinde bu ifadenin dışında kullanabileceğinizi unutmayın. Bu yüzden ya tüm kodunuzu aynı dinamik SQL dizesine sıkıştırmanız ya da gerçek bir tablo kullanmanız gerekir. Bu komut dosyalarının / prosedürlerin çoğunu aynı anda çalıştırabilmeniz gerekirse, bize rastgele bir tablo adı vermeniz gerekir, aksi takdirde birbirlerine basarlar. Gibi QUOTENAME(N'temp_'+CAST(NEWID() AS NVARCHAR(40))bir şey yeterince iyi bir isim olmalı.


Etrafındaki tüm verileri kopyalamak yerine, o tablodaki tüm enkarnasyonları tüm veritabanlarında birleştiren her tablo için yalnızca otomatik görünüm oluşturmak için benzer bir teknik kullanabilirsiniz. Ancak masa boyutuna bağlı olarak bu daha hızlı veya daha yavaş olabilir, bu yüzden test etmelisiniz. Bu rotaya gidersen, bu görüşleri ayrı bir veritabanına koyardım.


1
Bir #temp tablosu oluşturabilir ve daha sonra dinamik SQL'den başvurabilirsiniz. Sadece dinamik SQL çalıştırıldıktan sonra görünmez olması için bu kapsamda yarattıysanız.
Aaron Bertrand

3

SQLServerCentral makalesinde bunu elde etmek için iyi bir komut dosyası var:

Komut dosyasının en son sürümü burada da metin olarak mevcuttur (stormrage.com).

Keşke bütün senaryoyu buraya dahil etmenin bir yolu olsaydı, çünkü benim için çalışıyor. Senaryo buraya yapıştırmak için çok uzun.

Telif hakkı uyarısı:

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

İçindeki CREATE TABLEverilerden dinamik SQL kullanarak bir kaba oluşturabilirsiniz INFORMATION_SCHEMA.COLUMNS.

Kısıtlamalar vb. Eklemeniz gerekirse, diğer INFORMATION_SCHEMAgörünümlerden bazılarından bilgi eklemeniz gerekir .

Microsoft belgeleri

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.