你如何记录你的数据库?

use*_*316 238 sql-server documentation

我发现我的大多数客户根本没有记录他们的数据库,我觉得这很可怕。为了介绍一些更好的实践,我想知道人们正在使用哪些工具/流程。

  • 你如何记录你的数据库?(SQL 服务器)
  • 你用什么工具?
  • 数据库模式/元数据的文档存储格式?
    • Word文档
    • Excel电子表格
    • 纯文本
  • 文档流程或政策?

我不是在谈论逆向工程/记录现有数据库,而是主要讨论开发系统/数据库时的文档最佳实践。

Cad*_*oux 82

我一直在使用扩展属性,因为它们非常灵活。大多数标准文档工具都可以去掉MS_Description,然后您可以使用自定义工具使用自己的工具。

请参阅此演示文稿:#41-获取杠杆并选择任何海龟:使用元数据提升

这段代码:http : //code.google.com/p/caderoux/wiki/LeversAndTurtles

  • 您可以更改某些内容而忘记相应地更改扩展属性,从而使它们不正确。你能自动检测到这种差异吗? (4认同)
  • 至少,可以查询数据库模式 (sys.tables / sys.columns) 并左连接到其扩展属性 (sys.extended_properties) 以识别未记录的字段,然后将该脚本转换为测试以在部署时运行。 (3认同)

Eri*_*elp 60

Microsoft 的Visio Pro(直到 Visio 2010)可以像 CA 的ERwin一样对数据库进行逆向工程。Visio 是更便宜的选项,但 ERwin 是更详细、更完整的选项。扩展属性很好,如果人们不厌其烦地查看它们。您还可以使用 Red Gate 的SQL Doc之类的东西以 HTML 格式输出文档。

我发现命名约定和正确设置外键会导致几乎自文档化的数据库。您仍然应该有一些外部文档以更好地理解目的。

  • 一个简单的模式经常缺少的(即使在一个命名良好的外键数据库中)是字段的描述。根据我的经验,让所有字段都足够简单以适合列名是不寻常的。 (2认同)

ber*_*d_k 26

对于 SQL Server,我使用的是扩展属性。

使用以下 PowerShell 脚本,我可以为单个表或 dbo 架构中的所有表生成创建表脚本。

该脚本包含一个Create table命令、主键和索引。添加外键作为注释。表和表列的扩展属性作为注释添加。是的,支持多行属性。

该脚本已根据我的个人编码风格进行了调整。

  • 单列没有单独的排序规则。

  • 目前它需要 Sql Server 身份验证。

这是将扩展属性转换为良好的普通旧 ASCII 文档的完整代码(顺便说一句,重新创建表是有效的 sql):

function Get-ScriptForTable
{
    param (
        $server, 
        $dbname,
        $user,
        $password,
        $filter
    )

[System.reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | out-null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.ConnectionInfo")  | out-null

$conn = new-object "Microsoft.SqlServer.Management.Common.ServerConnection" 
$conn.ServerInstance = $server
$conn.LoginSecure = $false
$conn.Login = $user
$conn.Password = $password
$conn.ConnectAsUser = $false
$srv = New-Object "Microsoft.SqlServer.Management.Smo.Server" $conn

$Scripter = new-object ("Microsoft.SqlServer.Management.Smo.Scripter")
#$Scripter.Options.DriAll = $false
$Scripter.Options.NoCollation = $True
$Scripter.Options.NoFileGroup = $true
$scripter.Options.DriAll = $True
$Scripter.Options.IncludeIfNotExists = $False
$Scripter.Options.ExtendedProperties = $false
$Scripter.Server = $srv

$database = $srv.databases[$dbname]
$obj = $database.tables

$cnt = 1
$obj | % {

    if (! $filter -or  $_.Name -match $filter)
    {
        $lines = @()
        $header = "---------- {0, 3} {1, -30} ----------"  -f $cnt, $_.Name
        Write-Host $header 

        "/* ----------------- {0, 3} {1, -30} -----------------"  -f $cnt, $_.Name
        foreach( $i in $_.ExtendedProperties)
        {
            "{0}: {1}" -f $i.Name, $i.value
        }
        ""
        $colinfo = @{}
        foreach( $i in $_.columns)
        {
            $info = ""
            foreach ($ep in $i.ExtendedProperties)
            {
                if ($ep.value -match "`n")
                {
                    "----- Column: {0}  {1} -----" -f $i.name, $ep.name
                    $ep.value
                }
                else
                {
                    $info += "{0}:{1}  " -f $ep.name, $ep.value
                }
            }
            if ($info)
            {
                $colinfo[$i.name] =  $info
            }
        }
        ""
        "SELECT COUNT(*) FROM {0}" -f $_.Name
        "SELECT * FROM {0} ORDER BY 1" -f $_.Name
        "--------------------- {0, 3} {1, -30} ----------------- */" -f $cnt, $_.Name
        ""
        $raw = $Scripter.Script($_)
        #Write-host $raw
        $cont = 0
        $skip = $false 
        foreach ($line in $raw -split "\r\n")
        {
            if ($cont -gt 0)
            {
                if ($line -match "^\)WITH ")
                {
                    $line = ")"
                }
                $linebuf += ' ' + $line -replace " ASC", ""
                $cont--
                if ($cont -gt 0) { continue }
            }
            elseif ($line -match "^ CONSTRAINT ")
            {
                $cont = 3
                $linebuf = $line
                continue
            }
            elseif ($line -match "^UNIQUE ")
            {
                $cont = 3
                $linebuf = $line
                $skip = $true
                continue
            }
            elseif ($line -match "^ALTER TABLE.*WITH CHECK ")
            {
                $cont = 1
                $linebuf = "-- " + $line
                continue
            }
            elseif ($line -match "^ALTER TABLE.* CHECK ")
            {
                continue
            }
            else
            {
                $linebuf = $line
            }
            if ($linebuf -notmatch "^SET ")
            {
                if ($linebuf -match "^\)WITH ")
                {
                    $lines += ")"
                }
                elseif ($skip)
                {
                    $skip = $false
                }
                elseif ($linebuf -notmatch "^\s*$")
                {
                    $linebuf = $linebuf -replace "\]|\[", ""
                    $comment = $colinfo[($linebuf.Trim() -split " ")[0]]
                    if ($comment) { $comment = ' -- ' + $comment }
                    $lines += $linebuf + $comment
                }
            }
        }
        $lines += "go"
        $lines += ""
        $block = $lines -join "`r`n"
        $block
        $cnt++
        $used = $false
        foreach( $i in $_.Indexes)
        {
            $out = ''
            $raw = $Scripter.Script($i)
            #Write-host $raw
            foreach ($line in $raw -split "\r\n")
            {
                if ($line -match "^\)WITH ")
                {
                    $out += ")"
                }
                elseif ($line -match "^ALTER TABLE.* PRIMARY KEY")
                {
                    break
                }
                elseif ($line -match "^ALTER TABLE.* ADD UNIQUE")
                {
                    $out += $line -replace "\]|\[", "" -replace " NONCLUSTERED", "" 
                }
                elseif ($line -notmatch "^\s*$")
                {
                    $out += $line -replace "\]|\[", "" -replace "^\s*", "" `
                    -replace " ASC,", ", " -replace " ASC$", "" `
                    <#-replace "\bdbo\.\b", "" #> `
                    -replace " NONCLUSTERED", "" 
                }
                $used = $true
            }
            $block = "$out;`r`ngo`r`n"
            $out
        }
        if ($used)
        {
            "go"
        }
    }
} 
}
Run Code Online (Sandbox Code Playgroud)

您可以编写给定数据库的完整 dbo 架构的脚本

Get-ScriptForTable 'localhost'  'MyDB' 'sa' 'toipsecret'  |  Out-File  "C:\temp\Create_commented_tables.sql"
Run Code Online (Sandbox Code Playgroud)

或过滤单个表

Get-ScriptForTable 'localhost'  'MyDB' 'sa' 'toipsecret' 'OnlyThisTable'
Run Code Online (Sandbox Code Playgroud)


小智 22

看看SchemaCrawler - 它是我的免费命令行工具,我设计用来做你正在寻找的东西。SchemaCrawler 生成一个包含所有数据库架构对象的文本文件。此文本输出设计为人类可读,并且可以与来自另一台服务器的类似输出进行比较。

在实践中,我发现输出数据库模式的文本文件是有用的,当作为构建的一部分完成时。通过这种方式,您可以将文本文件签入源代码控制系统,并获得关于您的架构如何随时间演变的版本历史记录。SchemaCrawler 也旨在从命令行自动执行此操作。


Tan*_*ena 20

如果它曾经写过,则文档由一个 word 文档组成。将包括几个关系图。表格列表以及每个表格包含的内容以及它与其他表格的关系的简要说明。文档的一章包括安全设置:应用程序需要哪些“用户”权限?

通常,在我工作过的公司中,只有在客户是执行审计的人时才会编写数据库文档,这往往将其使用限制为金融和政府客户。

免责声明:太多的开发人员认为代码就是文档,我也为此感到内疚。

  • 我发现与代码没有紧密绑定的文档(例如,一个单独的 Word 文档,而不是自动生成的模式图 + 命名良好的数据库对象)的一个大问题是,这个文档肯定是完全错误的时间流逝。原因很简单:单独的文档有效地复制了信息。如果没有一种*自动化*的方式来保持它与源同步,它很快就会过时。将此与从数据库实时生成模式图并从代码中提取适当注释的工具进行比较。 (11认同)

jra*_*ara 16

我使用扩展属性和 Red Gates SQL Doc。效果很好!


fa1*_*n3r 14

有趣的是,我想知道其他人也是如何做到这一点的..

在开发我的第一个大型数据库项目时,我发现 Microsoft SQL Server Management Studio 10.0.1600.22 支持数据库图表,您可以将其导出到 word 文档或其他文档软件,您可以在其中添加尽可能多的文档细节。只需在 SQL Management Studio 上展开您连接的数据库,然后右键单击对象资源管理器中的“数据库图表”,然后选择“新建数据库图表”即可生成一个交互式图表,该图表将显示不同表之间的所有关系。您甚至可以指定要包含在图表中的表格,这样如果您只是试图逐个记录图像,则图像不会变得笨拙。将图像导出到任何其他编辑软件并根据需要进行评论。

我还在生成数据库的脚本中推荐大量 /注释/ 。

一般来说,写下它的全部目的是很多工作,但从长远来看,这是一个好主意,例如当您或其他一些可怜的灵魂在几年后回来更新您的创作时!:)

  • 我不使用 SQL Server 图表,因为外键约束只是在某处连接到表,就像在 [ER-diagrams](https://en.wikipedia.org/wiki/Entity%E2%80 %93relationship_model)。我更喜欢使用连接器连接主键和外键字段。 (3认同)

Car*_*est 13

我为所有对象设置了 MS_description 扩展属性,然后使用ApexSQL Doc记录整个数据库。我以前经常创建 HTML 文档,但最近我更喜欢 PDF


小智 12

我使用数据建模工具是因为它们允许我记录有关数据库的重要信息,而不是“适合”数据库的内容。元数据,如隐私/安全/敏感性问题、管理、治理等。

这可能超出了记录数据库的某些需要,但这些事情对业务很重要并帮助他们管理数据。

正式工具还帮助我管理存储在多个数据库/实例/服务器中的数据。这在我们的打包应用程序世界中从未如此真实。


小智 10

数据库字典创建器

是一个开源数据库文档工具,具有不错的 GUI 和导出/导入选项。它使用扩展属性来存储文档。它还将为主键列和外键列生成自动描述。

  • 自 2010 年以来未维护 (2认同)

Kin*_*hah 10

对于 Documenting sql server,我强烈推荐最近发布的:

使用 Windows PowerShell 的 SQL Server 和 Windows 文档由 Kendal Van Dyke 编写

来自链接的简要说明:

SQL Power Doc 是一组 Windows PowerShell 脚本和模块,用于发现、记录和诊断 SQL Server 实例及其底层 Windows 操作系统和机器配置。SQL Power Doc 适用于从 SQL Server 2000 到 2012 的所有版本的 SQL Server,以及从 Windows 2000 和 Windows XP 到 Windows Server 2012 和 Windows 8 的所有版本的 Windows Server 和消费者 Windows 操作系统。 SQL Power Doc 还能够记录Windows Azure SQL 数据库。


小智 8

事实上,扩展属性 (MS_Description) 是要走的路。将这些描述作为元数据的一部分随时可用,不仅可以被文档生成器利用,而且(希望有一天)可以被提供“智能感知”的工具利用,例如优秀的 Softtree 的 SQL 助手http://www.softtreetech.com/ isql.htm(上次我检查他们没有)或内置 SQL Sever Management Studio 的 Intellisense(自 sql2008)

我也相信开发人员和 DBA 应该很容易添加这些注释,因为正如 Tangurena 和 Nick Chammas 正确指出的那样 - 开发人员非常不愿意更新文档并且讨厌重复的工作 - 这足够公平,尤其是对于受过教育的人在整个职业生涯中优化事物。因此,除非在靠近源代码的地方更新文档真的很容易——否则这是行不通的。

在某些时候,我在网上搜索并没有找到解决方案,所以我写了 LiveDoco(不是免费的,抱歉)试图让它变得容易。如果有兴趣,点击此处了解更多信息:http : //www.livedoco.com/why-livedoco(网站 / LiveDoco 已于 2020 年 5 月 4 日停止使用)。


Rys*_*ian 5

我们使用Dataedo来创建数据字典、记录存储过程和函数。我们粘贴在 Visio 中创建的 ERD。所有文档都存储在 Dataedo 元数据存储库(格式化文本)中,我们将其导出为 HTML 以供内部使用或导出为 PDF 以供打印文档。

我们将每个对象分配给一个模块,并将每个模块分配给一个人。Dataedo 带有文档状态报告,因此我们可以判断是否有需要记录的新列或表。