您如何记录数据库?


227

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

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

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

Answers:


78

我一直在使用扩展属性,因为它们非常灵活。可以关闭大多数标准文档工具MS_Description,然后您可以将其与定制工具一起使用。

观看此演示文稿:#41-获取控制杆并选择任何乌龟:利用元数据进行提升

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


3
您可以更改某些内容,而忘记相应地更改扩展属性,从而使它们不正确。您可以自动检测出这种差异吗?
AK

2
至少,可以查询数据库架构(sys.tables / sys.columns)并保留其扩展属性(sys.extended_properties)的连接以标识未记录的字段,然后将该脚本转换为测试以在部署时运行。
米卡

59

Microsoft的Visio Pro(直到Visio 2010)可以像CA的ERwin一样对数据库进行反向工程。Visio是更便宜的选择,而ERwin是更详细,更完整的选择。如果人们不愿意去看扩展属性,那就更好了。您还可以使用Red Gate的SQL Doc之类的东西以HTML格式输出文档。

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


一个简单的模式经常丢失的(即使在一个命名良好且具有外键的数据库中)是对字段的描述。以我的经验,使所有字段都足够简单以适合列名是不寻常的。
–StockB


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"
        }
    }
} 
}

您可以使用脚本编写给定数据库的完整dbo模式

Get-ScriptForTable 'localhost'  'MyDB' 'sa' 'toipsecret'  |  Out-File  "C:\temp\Create_commented_tables.sql"

或过滤一张桌子

Get-ScriptForTable 'localhost'  'MyDB' 'sa' 'toipsecret' 'OnlyThisTable'

21

看一下SchemaCrawler-这是我设计的免费命令行工具,旨在满足您的需求。SchemaCrawler生成包含所有数据库模式对象的文本文件。此文本输出被设计为人类可读的,并且可与另一台服务器的类似输出进行区分。

实际上,我发现在作为构建的一部分完成时,输出数据库模式的文本文件很有用。这样,您可以将文本文件检入源代码控制系统,并拥有有关模式随时间演变的版本历史记录。SchemaCrawler也被设计为从命令行自动执行此操作。


20

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

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

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


10
我发现与文档没有紧密绑定的文档(例如,单独的Word文档,而不是自动生成的架构图+命名良好的数据库对象)存在的一个大问题是,该文档一定会被错误地排除,因为时间流逝。原因很简单:单独的文档可以有效地复制信息。如果没有自动的方法使其与源保持同步,它将很快变得过时。将其与从数据库实时生成模式图并从代码中提取适当注释的工具进行比较。
Nick Chammas


14

有趣的是,我想知道其他人也是如此。

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

我还建议在生成数据库的脚本中添加大量/ 注释 /。

通常,写下大量内容的工作很多,但是从长远来看是个好主意,例如当您或其他可怜的灵魂几年后回来更新您的创作时!:)


3
我不使用SQL Server图,因为外键约束只是连接到表的某个地方,就像在ER-图中一样。我更喜欢使用连接器来连接主键和外键字段。
R. Schreurs


12

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

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

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


10

对于Documenting sql server,我强烈建议您最近发布:

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

链接的简要说明:

SQL Power Doc是Windows PowerShell脚本和模块的集合,这些脚本和模块可发现,记录和诊断SQL Server实例及其基础的Windows OS和计算机配置。SQL Power Doc可用于从SQL Server 2000到2012的所有版本的SQL Server,以及从Windows 2000和Windows XP到Windows Server 2012和Windows 8的所有版本的Windows Server和使用者Windows操作系统。SQLPower Doc也能够提供文档Windows Azure SQL数据库。


10

DB Dictionary Creator

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


1
需要.NET Framework 4.0,并且只能与SQL Server和SQL Express一起使用
kevinsky,2012年

8

确实,扩展属性(MS_Description)是必经之路。将这些描述作为元数据的一部分随时可用,不仅可以由文档生成器使用,而且(希望有一天)可以由提供“智能”功能的工具(例如出色的Softtree的SQL Assistant http://www.softtreetech.com/)利用。 isql.htm(上次我检查它们没有)或内置于SQL Sever Management Studio的Intellisense(自sql2008起)

我还认为开发人员和DBA可以轻松添加这些注释,因为正如Tangurena和Nick Chammas正确指出的那样-开发人员非常不愿意保持文档更新并讨厌重复的工作-这对特别是受过培训的人来说是足够公平的在整个职业生涯中优化事物。因此,除非真的很容易在靠近源代码的位置更新文档,否则将无法正常工作。在某个时候,我在网上搜索并没有找到解决方案,所以我写了LiveDoco(不是免费的,对不起),试图使其变得简单。如果感兴趣,在这里了解更多信息:http : //www.livedoco.com/why-livedoco


7

您也可以看看wsSqlSrvDoc。这是一个很好的小工具,可与SQL Server扩展属性一起使用并创建MS Word文档。

所有列属性(具有外键关系)的打印输出都是开箱即用的。有关每个字段的进一步说明,您必须在SQL Server Management Studio中设置这些列的扩展属性。

它不是免费的,但价格合理。如果您只需要为“未进行中”数据库创建一个文档,那么该文档或多或少已经完成了,足以使用免费试用版。

工具网站


5

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

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


1

您可以--.sql文件中使用常规前缀的注释。

好处包括文档与数据库模式的代码一起提供,您可以轻松地将其提交到版本控制系统(例如Git)

例:

-- Table to store details about people.
-- See also: The customer table.
-- Note: Keep this data safe!
-- Todo: Add a email column.
CREATE TABLE Persons ( -- People in the registry
    PersonID int,
    LastName varchar(255), -- The person's last name
    FirstName varchar(255), -- The person's first name
    Address varchar(255), -- Address of residence
    City varchar(255) -- City of residence
);

也许您也可以使用XML。

-- <summary>
-- Table to store details about people.
-- </summary>
-- <column name="PersonID">The id column.</column>
-- <column name="LastName">The person's last name.</column>
-- <column name="FirstName">The person's first name.</column>
-- <column name="Address">Address of residence.</column>
-- <column name="City">City of residence.</column>
CREATE TABLE Persons (
    PersonID int,
    LastName varchar(255),
    FirstName varchar(255),
    Address varchar(255),
    City varchar(255)
);

您也可以使用类似于jsDoc / phpDoc的语法。

-- Table to store details about people.
-- @column {int} PersonID - The id column.
-- @column {varchar} LastName - The person's last name.
-- @column {varchar} FirstName - The person's first name.
-- @column {varchar} Address - Address of residence.
-- @column {varchar} City - City of residence.
-- @see {@link https://example.com/|Example}
-- @author Jane Smith <jsmith@example.com>
-- @copyright Acme 2018
-- @license BSD-2-Clause
-- @todo Add a column for email address.
-- @since 1.0.1
-- @version 1.2.3
CREATE TABLE Persons (
    PersonID int,
    LastName varchar(255),
    FirstName varchar(255),
    Address varchar(255),
    City varchar(255)
);

或者,您可以使用MarkDown语法。

-- # Persons
-- Table to store details about **people**.
-- * `PersonID` - The id column.
-- * `LastName` - The person's _last_ name.
-- * `FirstName` - The person's _first_ name.
-- * `Address` - Address of residence.
-- * `City` - City of residence.
--
-- [I'm an inline-style link](https://www.example.com/)
--
-- | PersonID | LastName | FirstName | Address | City |
-- | ---------| -------- | --------- | ------- | ---- |
-- | 1        | Smith    | Jane      | N/A     | N/A  |
CREATE TABLE Persons (
    PersonID int,
    LastName varchar(255),
    FirstName varchar(255),
    Address varchar(255),
    City varchar(255)
);

1

ERD图(数据库图)始终是我团队中最有用的

但是有规则在我们创建的每个表和列的属性中写入“ Decription ” 。

然后我们使用一个软件名称为Enterprise ArchitectTables与所有文档一起使用IndexesForeign KeysColumnsTypeDescription一起使用

在此处输入图片说明


-1

特别是对于MySQL,我们始终使用MySQL Workbench。我们在设计器中创建数据库设计,然后将其导出为可运行的SQL脚本。应用设计中的所有更改,然后运行生成的脚本,可以确保设计和实际数据库彼此之间完全同步,并且文档不会轻易过时。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.