【问题标题】:Generating sql code programmatically以编程方式生成 sql 代码
【发布时间】:2012-08-21 20:22:40
【问题描述】:

我通过任务菜单中的生成脚本手动生成脚本,方法是右键单击数据库。

现在我的问题是通过 c# 代码生成该脚本...

我的问题是

  1. 是否可以通过c#代码生成?

  2. 给我一​​些提示以完成?

等待您的宝贵建议和命令。

【问题讨论】:

  • 您是否已经熟悉使用 ExecuteRaeder() 和 ExecuteNonQuery();在 c# 中?如果是,那么您可以轻松解决。
  • 如果您希望自动生成脚本,这可能是一些艰难的过程或不可行。如果您可以编写脚本并且只想通过 c# 执行,那将很容易。你的情况如何?
  • @SamiAkram 你能提供一些基于它的提示吗.. 我尝试了这些链接但没有很好地执行codeproject.com/Articles/42209/…
  • @SamiAkram 我需要使用现有数据库自动生成脚本
  • @SamiAkram 我想首先使用现有数据库生成 bak 文件,然后通过 c#.net 代码运行生成的 bak 文件.. 有可能吗?

标签: c# sql .net sql-server tsql


【解决方案1】:

正如已经提到的,您可以使用 SMO 来执行此操作,这是一个使用 C# 编写数据库脚本的示例,我提到了几个选项,但正如 @David Brabant 的帖子中那样,您可以指定值有很多选择。

public string ScriptDatabase()
{
      var sb = new StringBuilder();

      var server = new Server(@"ServerName");
      var databse = server.Databases["DatabaseName"];

      var scripter = new Scripter(server);
      scripter.Options.ScriptDrops = false;
      scripter.Options.WithDependencies = true;
      scripter.Options.IncludeHeaders = true;
      //And so on ....


      var smoObjects = new Urn[1];
      foreach (Table t in databse.Tables)
      {
          smoObjects[0] = t.Urn;
          if (t.IsSystemObject == false)
          {
              StringCollection sc = scripter.Script(smoObjects);

              foreach (var st in sc)
              {
                  sb.Append(st);
              }
           }
       }
            return sb.ToString();
 }

这个link 可以帮助您获取和编写存储过程的脚本

【讨论】:

  • 在运行您的解决方案时,我收到错误消息“执行 Transact-SQL 语句或批处理时发生异常。”
  • 我只给出了服务器名称和数据库名称并运行你的代码我正在使用 sql 2008 你呢?
  • 是的,我更改了数据库名称,它执行得很好,现在我需要通过您的代码获取该数据库中曾经使用过的所有脚本是否可能
  • 为此您需要设置脚本选项,我的代码不包含所需的选项,建议您查看@David Brabant 帖子中的选项,它们具有相同的名称
  • 我发现以下有用的stackoverflow.com/questions/6453415/…
【解决方案2】:

您可以使用sql smo 来实现 SQL Server 企业管理器中提供的所有功能。有一个很好的教程here

编辑:在 PowerShell 中使用 SMO 的示例

function SQL-Script-Database
{
    <#
    .SYNOPSIS
    Script all database objects for the given database.

    .DESCRIPTION
    This  function scripts all database objects  (i.e.: tables,  views, stored
    procedures,  and user defined functions) for the specified database on the
    the given server\instance. It creates a subdirectory per object type under 
    the path specified.

    .PARAMETER savePath
    The root path where to save object definitions.

    .PARAMETER database
    The database to script (default = $global:DatabaseName)

    .PARAMETER DatabaseServer 
    The database server to be used (default: $global:DatabaseServer).

    .PARAMETER InstanceName 
    The instance name to be used (default: $global:InstanceName).

    .EXAMPLE
    SQL-Script-Database c:\temp AOIDB
    #>

    param (
        [parameter(Mandatory = $true)][string] $savePath,
        [parameter(Mandatory = $false)][string] $database = $global:DatabaseName,
        [parameter(Mandatory = $false)][string] $DatabaseServer = $global:DatabaseServer,
        [parameter(Mandatory = $false)][string] $InstanceName = $global:InstanceName
    )

    try
    {
        if (!$DatabaseServer -or !$InstanceName)
            { throw "`$DatabaseServer or `$InstanceName variable is not properly initialized" }

        $ServerInstance = SQL-Get-Server-Instance $DatabaseServer $InstanceName

        [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | Out-Null

        $s = New-Object Microsoft.SqlServer.Management.Smo.Server($ServerInstance)
        $db = $s.databases[$database]

        $objects = $db.Tables
        $objects += $db.Views
        $objects += $db.StoredProcedures
        $objects += $db.UserDefinedFunctions

        $scripter = New-Object ('Microsoft.SqlServer.Management.Smo.Scripter') ($s)

        $scripter.Options.AnsiFile = $true
        $scripter.Options.IncludeHeaders = $false
        $scripter.Options.ScriptOwner = $false
        $scripter.Options.AppendToFile = $false
        $scripter.Options.AllowSystemobjects = $false
        $scripter.Options.ScriptDrops = $false
        $scripter.Options.WithDependencies = $false
        $scripter.Options.SchemaQualify = $false
        $scripter.Options.SchemaQualifyForeignKeysReferences = $false
        $scripter.Options.ScriptBatchTerminator = $false

        $scripter.Options.Indexes = $true
        $scripter.Options.ClusteredIndexes = $true
        $scripter.Options.NonClusteredIndexes = $true
        $scripter.Options.NoCollation = $true

        $scripter.Options.DriAll = $true
        $scripter.Options.DriIncludeSystemNames = $false

        $scripter.Options.ToFileOnly = $true
        $scripter.Options.Permissions = $true

        foreach ($o in $objects | where {!($_.IsSystemObject)}) 
        {
            $typeFolder=$o.GetType().Name 

            if (!(Test-Path -Path "$savepath\$typeFolder")) 
                { New-Item -Type Directory -name "$typeFolder"-path "$savePath" | Out-Null }

            $file = $o -replace "\[|\]"
            $file = $file.Replace("dbo.", "")

            $scripter.Options.FileName = "$savePath\$typeFolder\$file.sql"
            $scripter.Script($o)
        }
    }

    catch
    {
        Util-Log-Error "`t`t$($MyInvocation.InvocationName): $_"
    }
}

【讨论】:

  • @david 您提供的链接创建表、sp 等并通过代码将其删除,但我的查询是通过 c#.net 获取特定数据库中使用的完整查询?有没有可能
  • 是的。而且是比较简单的。有关 PowerShell 中的示例,请参阅我编辑的帖子。将其翻译成 C# 应该很容易。
  • @DavidBrabant 谢谢大卫,但我不知道 powershell 如何翻译它
  • 我更新了示例链接以指向一个 C# 示例:mssqltips.com/sqlservertip/1833/…
【解决方案3】:

基于@Sami 回答

我创建了这个简单函数,它将为您的数据库生成所有脚本(表、视图、存储过程、用户和 UserDefinedFunctions)

第一名:Get Required Assemblies

C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies 是 正确的文件夹位置(或 C:\Program Files (x86)\Microsoft SQL 64 位系统上的服务器\100\SDK\Assemblies)。

你需要添加引用:

Microsoft.SqlServer.ConnectionInfo.dll

Microsoft.SqlServer.Smo.dll

Microsoft.SqlServer.Management.Sdk.Sfc.dll

Microsoft.SqlServer.SqlEnum.dll

第二:使用这个函数

  public static string ScriptDatabase() 
{
    // For Me Server is ".\SQLExpress" You may have changed
    Server myServer = new Server(@".\SQLExpress");
    Scripter scripter = new Scripter(myServer);

    //Databas1 is your database Name Thats Changable

    Database myAdventureWorks = myServer.Databases["MyDBName"];
    /* With ScriptingOptions you can specify different scripting  
    * options, for example to include IF NOT EXISTS, DROP  
    * statements, output location etc*/
    ScriptingOptions scriptOptions = new ScriptingOptions();
    scriptOptions.ScriptDrops = true;
   // scriptOptions.ScriptData = true;
    scriptOptions.ScriptSchema = true;


    scriptOptions.IncludeIfNotExists = true;
    string scrs = "";
    string tbScr = "";
    foreach (Table myTable in myAdventureWorks.Tables)
    {
        /* Generating IF EXISTS and DROP command for tables */
        StringCollection tableScripts = myTable.Script(scriptOptions);
        foreach (string script in tableScripts)
            scrs += script + "\n\n";

        /* Generating CREATE TABLE command */
        tableScripts = myTable.Script();
        foreach (string script in tableScripts)
            tbScr += script + "\n\n";
    }


    foreach (StoredProcedure mySP in myAdventureWorks.StoredProcedures)
    {
        /* Generating IF EXISTS and DROP command for StoredProcedures */
        StringCollection tableScripts = mySP.Script(scriptOptions);
        foreach (string script in tableScripts)
            scrs += script + "\n\n";

        /* Generating CREATE StoredProcedure command */
        tableScripts = mySP.Script(scriptOptions);
        foreach (string script in tableScripts)
            tbScr += script + "\n\n";
    }

    foreach (View myView in myAdventureWorks.Views)
    {
        /* Generating IF EXISTS and DROP command for Views */
        StringCollection tableScripts = myView.Script(scriptOptions);
        foreach (string script in tableScripts)
            scrs += script + "\n\n";

        /* Generating CREATE Views command */
        tableScripts = myView.Script(scriptOptions);
        foreach (string script in tableScripts)
            tbScr += script+"\n\n";
    }


    foreach (Microsoft.SqlServer.Management.Smo.User user in myAdventureWorks.Users)
    {
        /* Generating IF EXISTS and DROP command for Users */
        StringCollection tableScripts = user.Script(scriptOptions);
        foreach (string script in tableScripts)
            scrs += script+"\n\n";

        /* Generating CREATE Users command */
        tableScripts = user.Script(scriptOptions);
        foreach (string script in tableScripts)
            scrs += script + "\n\n";
    }



    foreach (Microsoft.SqlServer.Management.Smo.UserDefinedFunction userF in myAdventureWorks.UserDefinedFunctions)
    {
        /* Generating IF EXISTS and DROP command for UserDefinedFunctions */
        StringCollection tableScripts = userF.Script(scriptOptions);
        foreach (string script in tableScripts)
            scrs += script + "\n\n";

        /* Generating CREATE UserDefinedFunction command */
        tableScripts = userF.Script(scriptOptions);
        foreach (string script in tableScripts)
            scrs += script + "\n\n";
    } 

    // For WinForms
    return (scrs + "\n\n" + tbScr);
    //For Console
    //Console.WriteLine(scrs + "\n\n" + tbScr);
}

【讨论】:

  • 哪个选项允许我从表中导出数据?我试过 scriptOptions.ScriptData = true;但不知道如何循环导出数据
【解决方案4】:

我刚刚使用上述答案编写了一个命令行程序,用于在 c# 中执行此操作 并认为我会扩展上面的答案。

如果你想输出数据以及你需要使用的架构:

scripter.EnumScript(something);
//instead of 
scripter.Script(something);

脚本方法只是检查 IncludeData 选项,如果设置了它就会抛出异常,但你必须上 google 才能找到正确的方法!有趣的 API 设计!

数据库中的相关列表如下:

        database.Tables
        database.Schemas
        database.Views
        database.StoredProcedures
        database.UserDefinedFunctions
        database.Users
        database.Roles
        database.Sequences

虽然这可能并不详尽。

摆脱系统对象

这些对象中的列表都是IEnumerable but not IEnumerable&lt;T&gt; 的自定义类型,因此您不能对它们执行linq。这也意味着您必须找出其中的类型并使用 foreach 的隐式强制转换将它们取出。我以前从未在 c# 中使用过它,但我猜这东西可能是针对框架 2。

其中很多也有称为 IsSystemObject 的属性,但这并没有实现接口。起初,摆脱所有系统对象看起来会很痛苦,但您可以通过设置以下选项一举将它们全部剔除:

options.AllowSystemObjects = false;

这适用于除角色之外的所有内容,您必须手动执行系统角色:

        foreach (DatabaseRole role in database.Roles)
        {
            if(role.IsFixedRole)
                continue;

            list.Add(role);
        }

为输出添加对象

我使用的过程是创建一个 UrnCollection,然后将不同的列表添加到集合中。像这样:

        var list = new UrnCollection();

        foreach (Schema schema in database.Schemas)
            list.Add(schema.Urn);
        //... more of these

        scripter.EnumScript(list);

选项

从那里你需要弄清楚要设置哪些选项来重新创建你需要的输出。需要注意的几点:

  • 索引、触发器、约束等由选项设置,不被视为第一类对象。
  • 在 SSMS 的 UI 中,您根本无法生成序列,因此如果您使用这些序列,您的输出中至少会出现一些差异

有关如何获取外键等的更多信息,请参阅this post

健康警告

我开始将其视为复制数据库的一种方式,因为我认为备份和恢复不会达到我想要的效果。在沿着 smo 路径走了很长一段路并遇到了很多问题之后,我进行了一些重新评估,并找到了backup and restore is a lot simpler for that use case

【讨论】:

    【解决方案5】:

    希望它能指导您和即将到来的人。

    您必须在项目中添加以下四个引用以包含以下必需的命名空间

    添加引用

    1. 在解决方案资源管理器中右键单击您的项目并选择添加参考
    2. 点击上方菜单中的浏览
    3. 并按照以下说明选择 4 个 dll 文件

    Reference Microsoft.SqlServer.Smo.dll

    命名空间

    using System.Data.SqlClient;
    using System.Collections.Specialized;
    using Microsoft.SqlServer.Management.Smo;
    

    现在在任何函数或按钮点击事件中使用以下代码

            // For Me Server is ".\SQLExpress" You may have changed
            Server myServer = new Server(@".\SQLExpress");
            Scripter scripter = new Scripter(myServer);
    
            //Databas1 is your database Name Thats Changable
    
            Database myAdventureWorks = myServer.Databases["Database1"];
            /* With ScriptingOptions you can specify different scripting  
            * options, for example to include IF NOT EXISTS, DROP  
            * statements, output location etc*/
            ScriptingOptions scriptOptions = new ScriptingOptions();
            scriptOptions.ScriptDrops = true;
            scriptOptions.IncludeIfNotExists = true;
            string scrs = "";
            string tbScr = "";
            foreach (Table myTable in myAdventureWorks.Tables)
            {
                /* Generating IF EXISTS and DROP command for tables */
                StringCollection tableScripts = myTable.Script(scriptOptions);
                foreach (string script in tableScripts)
                    scrs += script;
    
                /* Generating CREATE TABLE command */
                tableScripts = myTable.Script();
                foreach (string script in tableScripts)
                    tbScr += script;
            }
            // For WinForms
            MessageBox.Show(scrs + "\n\n" + tbScr);
            //For Console
            //Console.WriteLine(scrs + "\n\n" + tbScr);
    

    它涉及http://www.mssqltips.com/sqlservertip/1833/generate-scripts-for-database-objects-with-smo-for-sql-server/ David Brabant 的回答(上)和上面的 SO 链接

    使用代码块 2。现在你也可以使用其他人了

    我在那里找不到myserver,但在上面的代码中也解决了。

    【讨论】:

    • @GowthamanSS 看看。试试看,看看有没有问题。但是我已经测试过了,它对我有用。
    【解决方案6】:

    获取存储过程脚本的示例。注意:我通过字符串操作添加“GO”和“ALTER”。我花了 6 个小时试图弄清楚为什么它不生成 GO。微软的反击:库 SMO(服务器管理对象)很糟糕,它有问题而且很难使用!

    var dbObj = database.StoredProcedures[name, schema];
    if (dbObj != null)
    {
        Console.WriteLine(dbObj);
    
        var script = new StringBuilder();
        script.AppendLine("USE LifelongLearning");
        script.AppendLine("GO");
        script.AppendLine();
    
        StringCollection scripts = dbObj.Script(scriptOptionsCreate);
        foreach (string s in scripts)
        {
            if (s.Contains("CREATE ", StringComparison.OrdinalIgnoreCase))
            {
                string s2 = s.Replace("CREATE ", "ALTER ", StringComparison.OrdinalIgnoreCase);
                script.AppendLine(s2);
            }
            else
            {
                script.AppendLine(s);
            }
    
            // it's a hack because scripting of the "GO" not working well
            if (s == "SET ANSI_NULLS ON" || s == "SET QUOTED_IDENTIFIER ON")
                script.AppendLine("GO");
        }
    
        script.AppendLine("GO");
    
        string fileName = schema + "." + name + ".sql";
        await File.WriteAllTextAsync(folder + "\\" + fileName, script.ToString());
    }
    
    readonly static ScriptingOptions scriptOptionsCreate = new ScriptingOptions()
    {
        //WithDependencies = true,  // will add create table before create view
        IncludeHeaders = false,  /****** Object:  View [dbo].[v_people]    Script Date: 9/30/2021 6:03:10 PM ******/
        ScriptSchema = true,
        ScriptData = false,
        Indexes = true,
        ClusteredIndexes = true,
        FullTextIndexes = true,
    
        // GO attempt
        FileName = "test.sql",
        ScriptBatchTerminator = true,
        NoCommandTerminator = false,
        //ToFileOnly = true,  // it makes script empty
        AppendToFile = true,
        EnforceScriptingOptions = true,
        AllowSystemObjects = true,
        Permissions = true,
        DriAllConstraints = true,
        SchemaQualify = true,
        AnsiFile = true,
        //AnsiPadding = false,
    };
    

    完整的工作应用程序在这里: https://github.com/sam-klok/CreateSQLScriptsFromDB/blob/main/CreateSQLScriptsFromDB/Program.cs

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-23
      • 1970-01-01
      • 2012-10-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多