SQL Server管理对象(SMO)中的默认约束不一致

Syb*_*eus 10 c# sql-server smo

我有程序使用SQL Server管理对象(SMO)为Microsoft SQL Server数据库生成DDL脚本.但是,根据服务器和数据库,我会收到表的默认约束的不一致输出.有时它们与CREATE TABLE声明一致,有时它们是独立的ALTER TABLE声明.我意识到两者都是有效且正确的SQL语句,但是如果没有一致性,它会阻止多个数据库的输出之间的自动比较,并阻止将输出添加到源控件以跟踪数据库模式的更改.如何确保默认约束的脚本输出的一致性?

示例程序

代码应该是直截了当的.打开服务器和数据库,然后为每个数据库对象生成单独的脚本文件,再生成一个包含整个数据库脚本的文件.我省略了很多错误检查和数据库对象,它们似乎已经生成了一致的输出.

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

内联语句的示例输出

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]
Run Code Online (Sandbox Code Playgroud)

独立语句的示例输出

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]
Run Code Online (Sandbox Code Playgroud)

似乎永远不会在单个数据库中混合,但可以在单个服务器上为每个数据库获得不同的输出样式.没有注意到它随着时间的推移而改变了数据库,但也许我没有尝试在足够长的时间内为数据库生成脚本.我已经备份并将数据库恢复到另一台服务器并以不同的名称恢复到同一服务器,它似乎随机决定选择一种输出样式.因此,当单个数据库还原可以表现出随机行为时,它似乎不是一个数据库设置.

目前,测试中使用的所有服务器都安装了SQL Server 2012,并且始终在安装了SQL Server Management Studio 2012的同一工作站上运行代码.我已经查看了MSDN 上ScriptingOptions的属性,但我没有看到任何突出的解决方案.

Syb*_*eus 3

经过进一步调查,我发现这是 SQL Server 管理对象 (SMO) 及其对 2012 及更高版本中默认约束的处理的问题。其他人报告了相关问题,例如以下 Microsoft Connect 问题:https://connect.microsoft.com/SQLServer/Feedback/Details/895113

虽然这可以解释为什么 SQL Server 管理对象 (SMO) 的默认约束不一致,但这并不是一个解决方案。在 Microsoft 解决问题之前,有人可能会确定一种解决方法来确保输出的一致性。因此,如果您能找到解决方法,这个问题仍然有其他答案。

  • 我的测试证实,向空表中添加一行会导致在表定义中指定默认值,而不是在单独的 alter 语句中指定。恼人的。 (2认同)