У меня есть программа, которая генерирует сценарии DDL для базы данных Microsoft SQL Server с использованием объектов управления SQL Server (SMO). Однако, в зависимости от сервера и базы данных, я получаю несогласованный вывод ограничений по умолчанию для таблиц. Иногда они встроены в оператор CREATE TABLE
, и иногда они являются автономными операторами ALTER TABLE
. Я понимаю, что оба являются действительными и правильными SQL-операторами, но без согласованности он предотвращает автоматическое сравнение вывода нескольких баз данных и предотвращает добавление вывода в исходный элемент управления для отслеживания изменений схемы базы данных. Как я могу обеспечить согласованность вывода script ограничений по умолчанию?
Пример программы
Код должен быть прямым. Открывает сервер и базу данных, затем генерирует отдельные файлы script для каждого объекта базы данных плюс еще один файл, содержащий script для всей базы данных. Я пропустил много ошибок и объектов базы данных, которые, похоже, уже генерируют согласованный вывод.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Common;
using System.Data.SqlClient;
using System.IO;
using System.Configuration;
using System.Runtime.Serialization;
using System.Data;
namespace Stackoverflow.Sample
{
class Program
{
public static void CreateScripts(SqlConnectionStringBuilder source, string destination)
{
Server sv = new Server(source.DataSource);
sv.ConnectionContext.LoginSecure = false;
sv.ConnectionContext.Login = source.UserID;
sv.ConnectionContext.Password = source.Password;
sv.ConnectionContext.ConnectionString = source.ConnectionString;
Database db = sv.Databases[source.InitialCatalog];
ScriptingOptions options = new ScriptingOptions();
options.ScriptData = false;
options.ScriptDrops = false;
options.ScriptSchema = true;
options.EnforceScriptingOptions = true;
options.Indexes = true;
options.IncludeHeaders = true;
options.ClusteredIndexes = true;
options.WithDependencies = false;
options.IncludeHeaders = false;
options.DriAll = true;
StringBuilder sbAll = new StringBuilder();
Dictionary<string, TriggerCollection> tableTriggers = new Dictionary<string, TriggerCollection>();
Dictionary<string, TriggerCollection> viewTriggers = new Dictionary<string, TriggerCollection>();
// Code omitted for Functions
// Tables
foreach (Table table in db.Tables)
{
StringBuilder sbTable = new StringBuilder();
foreach (string line in db.Tables[table.Name].Script(options))
{
sbAll.Append(line + "\r\n");
sbTable.Append(line + "\r\n");
Console.WriteLine(line);
}
// Write file with DDL of individual object
File.WriteAllText(Path.Combine(destination, table.Name + ".sql"), sbTable.ToString());
if (table.Triggers.Count > 0)
tableTriggers.Add(table.Name, table.Triggers);
}
// Code omitted for Views, Stored Procedures, Table Triggers, View Triggers, Database Triggers, etc
// Write file with full DDL of everything above
string[] statements = sbAll.ToString().Split(new string[] { "\r\nGO\r\n" }, StringSplitOptions.RemoveEmptyEntries);
File.WriteAllLines(Path.Combine(destination, "Full.sql"), statements);
}
}
}
Пример вывода строковых выражений
Пример того, как выглядит вывод, когда SMO создает сценарии с встроенными операторами для ограничений по умолчанию.
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
CREATE TABLE [dbo].[Products](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[StartDate] [date] NOT NULL,
[EndDate] [date] NULL,
[Name_En] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[Name_Fr] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[Type] [int] NOT NULL CONSTRAINT [DF_Products_Type] DEFAULT ((0)),
[ManagedType] [int] NOT NULL CONSTRAINT [DF_Products_ManagedType] DEFAULT ((0)),
[ProductFamilyID] [bigint] NOT NULL,
[ImplementationID] [bigint] NOT NULL,
CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [dbo].[Products] WITH CHECK ADD CONSTRAINT [FK_Products_Implementations] FOREIGN KEY([ImplementationID])
REFERENCES [dbo].[Implementations] ([ID])
ALTER TABLE [dbo].[Products] CHECK CONSTRAINT [FK_Products_Implementations]
ALTER TABLE [dbo].[Products] WITH CHECK ADD CONSTRAINT [FK_Products_ProductFamilies] FOREIGN KEY([ProductFamilyID])
REFERENCES [dbo].[ProductFamilies] ([ID])
ALTER TABLE [dbo].[Products] CHECK CONSTRAINT [FK_Products_ProductFamilies]
Пример вывода автономных отчетов
Пример того, как выглядит вывод, когда SMO генерирует скрипты с автономными операторами для ограничений по умолчанию.
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
CREATE TABLE [dbo].[Products](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[StartDate] [date] NOT NULL,
[EndDate] [date] NULL,
[Name_En] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[Name_Fr] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[Type] [int] NOT NULL,
[ManagedType] [int] NOT NULL,
[ProductFamilyID] [bigint] NOT NULL,
[ImplementationID] [bigint] NOT NULL,
CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [dbo].[Products] ADD CONSTRAINT [DF_Products_Type] DEFAULT ((0)) FOR [Type]
ALTER TABLE [dbo].[Products] ADD CONSTRAINT [DF_Products_ManagedType] DEFAULT ((0)) FOR [ManagedType]
ALTER TABLE [dbo].[Products] WITH CHECK ADD CONSTRAINT [FK_Products_Implementations] FOREIGN KEY([ImplementationID])
REFERENCES [dbo].[Implementations] ([ID])
ALTER TABLE [dbo].[Products] CHECK CONSTRAINT [FK_Products_Implementations]
ALTER TABLE [dbo].[Products] WITH CHECK ADD CONSTRAINT [FK_Products_ProductFamilies] FOREIGN KEY([ProductFamilyID])
REFERENCES [dbo].[ProductFamilies] ([ID])
ALTER TABLE [dbo].[Products] CHECK CONSTRAINT [FK_Products_ProductFamilies]
Никогда не кажется, что это смесь в одной базе данных, но может иметь разные стили вывода для каждой базы данных на одном сервере. Не заметили, что со временем это изменилось для базы данных, но, возможно, я просто не пытался генерировать сценарии для базы данных в течение достаточно длительного периода времени. Я создал резервную копию и восстановил базу данных на другом сервере и на том же сервере под другим именем, и, похоже, случайным образом решил выбрать один стиль вывода. Поэтому не похоже, что это может быть параметр базы данных, когда восстановление отдельных баз данных может проявлять случайное поведение.
В настоящее время на всех серверах, используемых при тестировании, установлен SQL Server 2012 и всегда выполняется код на той же рабочей станции, где установлен SQL Server Management Studio 2012. Я просмотрел свойства ScriptingOptions в MSDN, и я не вижу ничего, что выделяется как решение.