关于sql server:源代码管理中的存储过程/数据库模式

关于sql server:源代码管理中的存储过程/数据库模式

Stored procedures/DB schema in source control

你们在你选择的源代码管理系统中跟踪存储过程和数据库架构吗?

当您进行更改(添加表,更新存储过程,如何将更改导入源代码管理?

我们在工作中使用SQL Server,并且我已经开始使用darc进行版本控制,但我对一般策略以及任何方便的工具都很好奇。

编辑:哇,谢谢所有伟大的建议,伙计们! 我希望我能选择多个"已接受的答案"!


我们选择编写所有脚本,包括所有存储过程和架构更改。没有所见即所得的工具,也不需要花哨的"同步"程序。

架构更改很简单,您只需要为该版本创建和维护单个文件,包括所有架构和数据更改。这将成为您从版本x到x + 1的转换脚本。然后,您可以针对生产备份运行它,并将其集成到"每日构建"中,以验证它是否正常运行。请注意,重要的是不要更改或删除已经编写的模式/数据加载sql,因为您最终可能会破坏以后编写的任何sql。

1
2
3
4
5
6
7
-- change #1234
ALTER TABLE asdf ADD COLUMN MyNewID INT
GO

-- change #5678
ALTER TABLE asdf DROP COLUMN SomeOtherID
GO

对于存储过程,我们为每个sproc选择一个文件,并使用drop / create表单。在部署时重新创建所有存储过程。缺点是如果在源代码管理之外进行了更改,则更改将丢失。同时,对于任何代码都是如此,但您的DBA需要注意这一点。这确实会阻止团队外的人员使用您的存储过程,因为他们的更改会在升级过程中丢失。

使用Sql Server,语法如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[usp_MyProc]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [usp_MyProc]
GO

CREATE PROCEDURE [usp_MyProc]
(
    @UserID INT
)
AS

SET NOCOUNT ON

-- stored procedure logic.

SET NOCOUNT OFF

GO

剩下要做的唯一事情就是编写一个实用程序来整理所有单个文件,并使用整个更新集创建一个新文件(作为单个脚本)。通过首先添加架构更改然后递归目录结构并包括所有存储过程文件来执行此操作。

作为编写脚本的好处,您将更好地阅读和编写SQL。您还可以使整个过程更加精细,但这是如何在没有任何特殊软件的情况下对所有sql进行源代码控制的基本格式。

附录:Rick是正确的,您将失去对DROP / CREATE存储过程的权限,因此您可能需要编写另一个脚本才能重新启用特定权限。此权限脚本将是最后运行的脚本。我们的经验发现ALTER与DROP / CREATE语义有关的问题。因人而异


在Visual Studio中创建"数据库项目"以编写和管理您的sQL代码,并将项目与版本控制一起保留在解决方案的其余部分中。


我们上一份工作中使用的解决方案是将脚本编号添加到源代码控制中:

01.CreateUserTable.sql
02.PopulateUserTable
03.AlterUserTable.sql
04.CreateOrderTable.sql

我们的想法是,我们始终知道运行脚本的顺序,并且我们可以避免必须管理在您尝试修改脚本#1时可能出现的数据完整性问题(这可能会导致#2中的INSERT失败)。


我同意(并赞成)罗伯特保尔森的做法。这是假设您掌握了一个开发团队的责任和纪律,以坚持这样的做法。

为了"强制"到我的团队,我们的解决方案至少从Visual Studio Team Edition for Database Professionals维护一个数据库项目。与解决方案中的其他项目一样,数据库项目获得版本控制。它使得将数据库中的所有内容分解为可维护的块,这是一个自然的开发过程,在此过程中"训练"我的团队。

当然,作为一个Visual Studio项目,它不是完美的。你会遇到很多可能会让你感到沮丧或困惑的怪癖。在完成任务之前,需要了解项目的工作原理。例子包括

  • 从CSV文件部署数据。
  • 基于构建类型选择性部署测试数据。
  • 与嵌入在其中的某种类型的CLR程序集的数据库进行比较时,Visual Studio崩溃。
  • 实现不同身份验证方案的测试/生产数据库之间没有差异 - SQL用户与Active Directory用户。

但是对于没有对数据库对象进行版本控制的团队,这是一个良好的开端。另一个着名的替代方案当然是Red Gate的SQL Server产品套件,大多数使用它们的人都认为它优于微软的产品。


在SQL Server中删除/创建脚本时要记住的一件事是对象级权限将丢失。我们改变了标准,改为使用ALTER脚本来维护这些权限。

还有一些其他注意事项,例如删除对象会丢弃sp_depends使用的依赖记录,而创建对象只会创建该对象的依赖项。因此,如果删除/创建视图,sp_depends将不再知道引用该视图的任何对象。

故事的道德,使用ALTER脚本。


我认为你应该编写一个自动设置数据库的脚本,包括任何存储过程。然后应将此脚本放在源代码管理中。


从我的经历中结合不同的观点。在Oracle世界中,一切都是通过"创建"DDL脚本来管理的。正如ahockley所提到的,每个对象都有一个脚本。如果对象需要更改,则会修改其DDL脚本。有一个包装器脚本可以调用所有对象脚本,以便您可以将当前的数据库构建部署到所需的任何环境中。这是主要的核心创造。

显然在实时应用程序中,无论何时推送需要新列的新构建,您都不会删除表并创建新表。您将要执行ALTER脚本并添加列。所以每次这种变化都需要发生时,总有两件事要做:1)编写alter DDL和2)更新核心创建DDL以反映变化。两者都进入源代码控制,但是单个alter脚本更像是一个瞬间的时间点,因为它只会用于应用delta。

您还可以使用像ERWin这样的工具来更新模型并转发生成DDL,但我知道的大多数DBA都不相信建模工具可以完全按照自己的方式生成脚本。你也可以使用ERWin定期将你的核心DDL脚本反向工程到一个模型中,但要让它看起来很正确(每次爆炸时你都这么做)会很麻烦。

在Microsoft世界中,我们采用了类似的策略,但我们使用Red Gate产品来帮助管理脚本和增量。仍将脚本放在源代码管理中。每个对象仍然有一个脚本(表,sproc,等等)。最初,一些DBA确实更喜欢使用SQL Server GUI来管理对象而不是使用脚本。但这使得企业在成长过程中始终如一地管理非常困难。

如果DDL在源代码控制中,那么使用任何构建工具(通常是ant)来编写部署脚本都是微不足道的。


我发现到目前为止,最简单,最快速和最安全的方法就是咬紧牙关并使用RedGate的SQL Source Control。在几分钟内脚本化并存储在存储库中。我只是希望RedGate将该产品视为亏损的领导者,以便它可以得到更广泛的使用。


与上面的Robert Paulson类似,我们的组织将数据库保持在源代码管理之下。但是,我们的不同之处在于我们试图限制我们拥有的脚本数量。

对于任何新项目,都有一套程序。我们在版本1上有一个模式创建脚本,一个存储过程创建脚本,可能还有一个初始数据加载创建脚本。所有的过程都被保存在一个公认的大量文件中。如果我们使用Enterprise Library,我们会包含一个用于记录的创建脚本的副本;如果它是使用ASP.NET应用程序框架的ASP.NET项目(身份验证,个性化等),我们也会包含该脚本。 (我们从微软的工具中生成它,然后调整它直到它在不同网站上以可复制的方式工作。不好玩,但是宝贵的时间投资。)

我们使用神奇的CTRL + F来找到我们喜欢的过程。 :)(如果SQL Management Studio像VS那样有代码导航,我们会喜欢它。叹气!)

对于后续版本,我们通常使用upgradeSchema,upgradeProc和/或updateDate脚本。对于模式更新,我们尽可能地更改表,根据需要创建新表。对于proc更新,我们DROP和CREATE。

这种方法会出现一个皱纹。生成数据库很容易,并且很容易在当前的数据库版本上获得新的数据库。但是,必须注意DAL生成(我们目前 - 通常使用SubSonic),以确保DB / schema / proc更改与用于访问它们的代码完全同步。但是,在我们的构建路径中是一个生成SubSonic DAL的批处理文件,所以我们的SOP是检查DAL代码,重新运行该批处理文件,然后在架构和/或转换发生更改时随时检查它。 (当然,这会触发源代码构建,将共享依赖项更新到相应的DLL ...)


在过去的经验中,我保持数据库更改源的控制,以便对于产品的每个版本,任何数据库更改都始终编写并存储在我们正在处理的版本中。现有的构建过程将根据存储每个"应用程序"的当前版本的数据库中的表自动将数据库升级到当前版本。然后,我们编写的自定义.net实用程序应用程序将运行并确定数据库的当前版本,并按脚本的前缀编号顺序运行任何新脚本。然后我们进行单元测试以确保一切都很好。

我们将脚本存储在源代码管理中,如下所示(下面的文件夹结构):

关于表和存储过程的当前命名约定,我有点生气,因为我的例子......

[根]
     [应用]
         [版]
             [脚本]

脚本
    所有MyApplication
         1.2.1
             001.MyTable.Create.sql
             002.MyOtherTable.Create.sql
             100.dbo.usp.MyTable.GetAllNewStuff.sql

通过使用将考虑应用程序和版本的Versions表,应用程序将还原每周生产备份,并运行自当前版本以来对数据库所需的所有脚本。通过使用.net,我们很容易将其打包成一个事务,如果有任何失败,我们会回滚,并发送电子邮件,所以我们知道发布有坏脚本。

因此,所有开发人员都会确保在源代码管理中保持这一点,因此协调版本将确保我们计划针对数据库运行的所有脚本都能成功运行。

这可能是您所寻找的更多信息,但它对我们来说非常有效,并且考虑到结构,很容易让所有开发人员参与其中。

当发布日到来时,运营团队将遵循发布说明并从源代码控制中获取脚本,并使用我们在夜间构建过程中使用的.net应用程序对数据库运行包,这将自动将脚本打包在事务中,这样如果一些失败它会自动回滚,并没有对数据库产生任何影响。


如果存在drop / create语句,则存储过程每个sp获得1个文件。视图和函数也可以获取自己的文件,因此更容易进行版本和重用。

Schema是一个开始的脚本,然后我们将进行版本更改。

所有这些都存储在一个Visual Studio数据库项目中,该项目连接到TFS(@ work或VisualSVN Server @ home用于个人资料),文件夹结构如下:
- 项目
- 功能
- 架构
- 存储过程
- 意见


在我的公司,我们倾向于将源控件中的所有数据库项目存储为单独的脚本,就像单个代码文件一样。首先在数据库中进行任何更新,然后将其迁移到源代码存储库中,以便维护更改历史记录。
第二步,将所有数据库更改迁移到集成数据库。此集成数据库准确表示生产数据库在部署后应该是什么样子。我们还有一个QA数据库,代表当前的生产状态(或最后的部署)。在Integration数据库中进行所有更改后,我们使用模式差异工具(Red Gate的SQL Server SQL Diff)生成一个脚本,该脚本将所有更改从一个数据库迁移到另一个数据库。
我们发现这相当有效,因为它生成了一个我们可以轻松地与我们的安装程序集成的脚本。我们经常遇到的最大问题是开发人员忘记将他们的更改迁移到集成中。


我运行一个作业来编写一个正式的目录结构。

以下是VS2005代码,命令行项目,从批处理文件调用,完成工作。代码末尾的app.config键。

它基于我在网上找到的其他代码。设置起来有点痛苦,但是一旦你开始工作就会很好。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
Imports Microsoft.VisualStudio.SourceSafe.Interop
Imports System
Imports System.Configuration

Module Module1

    Dim sourcesafeDataBase As String, sourcesafeUserName As String, sourcesafePassword As String, sourcesafeProjectName As String, fileFolderName As String


    Sub Main()
        If My.Application.CommandLineArgs.Count > 0 Then
            GetSetup()
            For Each thisOption As String In My.Application.CommandLineArgs
                Select Case thisOption.ToUpper
                    Case"CHECKIN"
                        DoCheckIn()
                    Case"CHECKOUT"
                        DoCheckOut()
                    Case Else
                        DisplayUsage()
                End Select
            Next
        Else
            DisplayUsage()
        End If
    End Sub

    Sub DisplayUsage()
        Console.Write(System.Environment.NewLine +"Usage: SourceSafeUpdater option" + System.Environment.NewLine + _
           "CheckIn - Check in ( and adds any new ) files in the directory specified in .config" + System.Environment.NewLine + _
           "CheckOut - Check out all files in the directory specified in .config" + System.Environment.NewLine + System.Environment.NewLine)
    End Sub

    Sub AddNewItems()
        Dim db As New VSSDatabase
        db.Open(sourcesafeDataBase, sourcesafeUserName, sourcesafePassword)
        Dim Proj As VSSItem
        Dim Flags As Integer = VSSFlags.VSSFLAG_DELTAYES + VSSFlags.VSSFLAG_RECURSYES + VSSFlags.VSSFLAG_DELNO
        Try
            Proj = db.VSSItem(sourcesafeProjectName, False)
            Proj.Add(fileFolderName,"", Flags)
        Catch ex As Exception
            If Not ex.Message.ToString.ToLower.IndexOf("already exists") > 0 Then
                Console.Write(ex.Message)
            End If
        End Try
        Proj = Nothing
        db = Nothing
    End Sub

    Sub DoCheckIn()
        AddNewItems()
        Dim db As New VSSDatabase
        db.Open(sourcesafeDataBase, sourcesafeUserName, sourcesafePassword)
        Dim Proj As VSSItem
        Dim Flags As Integer = VSSFlags.VSSFLAG_DELTAYES + VSSFlags.VSSFLAG_UPDUPDATE + VSSFlags.VSSFLAG_FORCEDIRYES + VSSFlags.VSSFLAG_RECURSYES
        Proj = db.VSSItem(sourcesafeProjectName, False)
        Proj.Checkin("", fileFolderName, Flags)
        Dim File As String
        For Each File In My.Computer.FileSystem.GetFiles(fileFolderName)
            Try
                Proj.Add(fileFolderName + File)
            Catch ex As Exception
                If Not ex.Message.ToString.ToLower.IndexOf("access code") > 0 Then
                    Console.Write(ex.Message)
                End If
            End Try
        Next
        Proj = Nothing
        db = Nothing
    End Sub

    Sub DoCheckOut()
        Dim db As New VSSDatabase
        db.Open(sourcesafeDataBase, sourcesafeUserName, sourcesafePassword)
        Dim Proj As VSSItem
        Dim Flags As Integer = VSSFlags.VSSFLAG_REPREPLACE + VSSFlags.VSSFLAG_RECURSYES
        Proj = db.VSSItem(sourcesafeProjectName, False)
        Proj.Checkout("", fileFolderName, Flags)
        Proj = Nothing
        db = Nothing
    End Sub

    Sub GetSetup()
        sourcesafeDataBase = ConfigurationManager.AppSettings("sourcesafeDataBase")
        sourcesafeUserName = ConfigurationManager.AppSettings("sourcesafeUserName")
        sourcesafePassword = ConfigurationManager.AppSettings("sourcesafePassword")
        sourcesafeProjectName = ConfigurationManager.AppSettings("sourcesafeProjectName")
        fileFolderName = ConfigurationManager.AppSettings("fileFolderName")

    End Sub

End Module

我们将存储过程保存在源代码管理中


我们在当前项目中一直使用替代方法 - 我们没有在源代码控制下使用数据库,而是在我们到达每个版本时使用数据库差异工具来编写更改脚本。
到目前为止,它一直运作良好。


我强烈建议在源代码管理中维护架构和存储过程。

保持存储过程版本化允许在确定存在问题时回滚它们。

根据您的意思,Schema是一个不太明显的答案。对于复制环境(prod / dev / user等),维护在源代码管理中定义表的SQL非常有用。


我们将存储过程保留在源代码管理中。我们(或至少我)这样做的方法是在我的项目中添加一个文件夹,为每个SP添加一个文件并手动复制,将代码粘贴到其中。所以当我更改SP时,我手动需要更改源控件的文件。

我很想知道人们是否可以自动执行此操作。


对于procs,将带有脚本包装器的procs写入普通文件,并应用这些文件中的更改。如果它正确应用,那么您可以签入该文件,并且您也可以从该文件中重现它。

对于架构更改,您可能需要签入脚本以逐步进行所做的更改。编写脚本,应用它,然后将其签入。然后,您可以构建一个流程,以自动应用每个模式脚本。


编写所有内容(对象创建等)并将这些脚本存储在源代码管理中。变化是如何实现的?这是事情如何完成的标准做法的一部分。需要添加表吗?编写CREATE TABLE脚本。更新一个sproc?编辑存储过程脚本。

我更喜欢每个对象一个脚本。


我们在SCM中存储与应用程序相关的所有内容。数据库脚本通常存储在他们自己的项目中,但其处理方式与任何其他代码一样......设计,实现,测试,提交。


如果您正在寻找一个简单的现成解决方案,我们的Sql Historian系统使用后台进程自动将DDL更改同步到TFS或SVN,对任何对数据库进行更改的人都是透明的。根据我的经验,最大的问题是使用服务器上的更改来维护源代码控制中的代码 - 这是因为通常你必须依赖人(开发人员,甚至!)来改变他们的工作流程并记住检查他们的更改在他们已经在服务器上制作之后。将这种负担放在机器上会让每个人的生活更轻松。


推荐阅读