Чи є спосіб створити скрипт створення таблиці в 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

Однак сценарій вище не вдається наступним повідомленням:

Явне значення стовпця ідентичності в таблиці "#tmp" можна вказати лише тоді, коли використовується список стовпців та ввімкнено IDENTITY_INSERT.

Додавання цього:

SET IDENTITY_INSERT #tmp ON

не допомагає, оскільки я не можу вказати список стовпців і зберегти його загальним.

У SQL немає можливості вимкнути ідентифікацію даної таблиці. Ви можете лише скинути стовпчик і додати стовпчик, який, очевидно, змінює порядок стовпців. І якщо порядок стовпців зміниться, вам, знову ж таки, потрібно вказати список стовпців, який би відрізнявся залежно від таблиці, яку ви запитуєте.

Тому я думав, чи зможу я отримати сценарій створення таблиці у своєму T-SQL-коді, я міг би маніпулювати нею за допомогою рядкових маніпуляційних виразів, щоб видалити стовпець ідентичності, а також додати стовпчик для імені бази даних до набору результатів.

Чи може хтось придумати порівняно простий спосіб досягти того, що я хочу?

Відповіді:


28

Ще в 2007 році я попросив простіший спосіб створення CREATE TABLEсценарію через T-SQL, а не використання інтерфейсу або 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.

(Це довіряє вам, що схема однакова; вона не підтверджує, що одна чи кілька таблиць з тих пір були змінені, і можуть вийти з ладу.)


Чому це не створює специфікацій ідентичності?
FindOutIslamNow

1
@Kilanny Ви прочитали всю відповідь, як частину, де я розповідаю про те, чому ми ігноруємо власність особи?
Аарон Бертран

Мені потрібно ПОВНОЕ визначення таблиці (включаючи ідентичність, індекси, обмеження, ..). На щастя, я знайшов цей чудовий сценарій stormrage.com/SQLStuff/sp_GetDDLa_Latest.txt Дякую все одно
FindOutIslamNow

@Kilanny Great. Щоб було зрозуміло, ваша вимога не відповідає вимозі цього питання. Їм потрібна була копія таблиці без ідентичності, оскільки вони використовували її для копіювання наявних даних, а не для генерування нових рядків.
Аарон Бертран

Аарон, це елегантне рішення в крайній частині, де вам не потрібні ключі тощо ...
GWR

5

Неможливо в 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

Налаштування схеми 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.
Аарон Бертран

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.