简体   繁体   English

了解 SQL Server 中所有数据库表之间的关系

[英]Know relationships between all the tables of database in SQL Server

I wish to all know how the tables in my database are related to each other (ie PK/FK/UK) and hence i created a database diagram of all my tables in SQL Server.我想知道我的数据库中的表是如何相互关联的(即 PK/FK/UK),因此我在 SQL Server 中创建了我所有表的数据库图。 The diagram that was created was not easily readable and had to scroll (horizontally and sometimes vertically) to see the table on the other end.创建的图表不易阅读,必须滚动(水平滚动,有时垂直滚动)才能看到另一端的表格。

In short SQL's db diagram are not UI friendly when it comes to knowing relationships between many tables.简而言之,在了解许多表之间的关系时,SQL 的数据库图对 UI 不友好。

My (simple) Question: Is there something like database diagram which can do what db diagram did but in "good" way?我的(简单)问题:是否有类似数据库图的东西可以做数据库图所做的但以“好”的方式?

Sometimes, a textual representation might also help;有时,文本表示也可能有所帮助; with this query on the system catalog views, you can get a list of all FK relationships and how the link two tables (and what columns they operate on).通过对系统目录视图的此查询,您可以获得所有 FK 关系的列表以及如何链接两个表(以及它们操作的列)。

SELECT
    fk.name 'FK Name',
    tp.name 'Parent table',
    cp.name, cp.column_id,
    tr.name 'Refrenced table',
    cr.name, cr.column_id
FROM 
    sys.foreign_keys fk
INNER JOIN 
    sys.tables tp ON fk.parent_object_id = tp.object_id
INNER JOIN 
    sys.tables tr ON fk.referenced_object_id = tr.object_id
INNER JOIN 
    sys.foreign_key_columns fkc ON fkc.constraint_object_id = fk.object_id
INNER JOIN 
    sys.columns cp ON fkc.parent_column_id = cp.column_id AND fkc.parent_object_id = cp.object_id
INNER JOIN 
    sys.columns cr ON fkc.referenced_column_id = cr.column_id AND fkc.referenced_object_id = cr.object_id
ORDER BY
    tp.name, cp.column_id

Dump this into Excel, and you can slice and dice - based on the parent table, the referenced table or anything else.将其转储到 Excel 中,您可以根据父表、引用表或其他任何内容进行切片和切块。

I find visual guides helpful - but sometimes, textual documentation is just as good (or even better) - just my 2 cents.....我发现视觉指南很有帮助 - 但有时,文本文档也一样好(甚至更好) - 只是我的 2 美分.....

Just another way to retrieve the same data using INFORMATION_SCHEMA使用 INFORMATION_SCHEMA 检索相同数据的另一种方法

The information schema views included in SQL Server comply with the ISO standard definition for the INFORMATION_SCHEMA. SQL Server 中包含的信息架构视图符合 INFORMATION_SCHEMA 的 ISO 标准定义。

sqlauthority way sqlauthority方式

SELECT
K_Table = FK.TABLE_NAME,
FK_Column = CU.COLUMN_NAME,
PK_Table = PK.TABLE_NAME,
PK_Column = PT.COLUMN_NAME,
Constraint_Name = C.CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME
INNER JOIN (
SELECT i1.TABLE_NAME, i2.COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS i1
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE i2 ON i1.CONSTRAINT_NAME = i2.CONSTRAINT_NAME
WHERE i1.CONSTRAINT_TYPE = 'PRIMARY KEY'
) PT ON PT.TABLE_NAME = PK.TABLE_NAME
---- optional:
ORDER BY
1,2,3,4
WHERE PK.TABLE_NAME='something'WHERE FK.TABLE_NAME='something'
WHERE PK.TABLE_NAME IN ('one_thing', 'another')
WHERE FK.TABLE_NAME IN ('one_thing', 'another')

或者你可以看看schemacrawler

This stored procedure will provide you with a hierarchical tree of relationship.此存储过程将为您提供层次关系树。 Based on this article from Technet.基于 Technet 的这篇文章 It will also optionally provide you a query for reading or deleting all the related data.它还可以选择为您提供读取或删除所有相关数据的查询。

IF OBJECT_ID('GetForeignKeyRelations','P') IS NOT NULL 
    DROP PROC GetForeignKeyRelations 
GO 

CREATE PROC GetForeignKeyRelations 
@Schemaname Sysname = 'dbo' 
,@Tablename Sysname 
,@WhereClause NVARCHAR(2000) = '' 
,@GenerateDeleteScripts bit  = 0  
,@GenerateSelectScripts bit  = 0 

AS 

SET NOCOUNT ON 

DECLARE @fkeytbl TABLE 
( 
ReferencingObjectid        int NULL 
,ReferencingSchemaname  Sysname NULL 
,ReferencingTablename   Sysname NULL  
,ReferencingColumnname  Sysname NULL 
,PrimarykeyObjectid     int  NULL 
,PrimarykeySchemaname   Sysname NULL 
,PrimarykeyTablename    Sysname NULL 
,PrimarykeyColumnname   Sysname NULL 
,Hierarchy              varchar(max) NULL 
,level                  int NULL 
,rnk                    varchar(max) NULL 
,Processed                bit default 0  NULL 
); 



 WITH fkey (ReferencingObjectid,ReferencingSchemaname,ReferencingTablename,ReferencingColumnname 
            ,PrimarykeyObjectid,PrimarykeySchemaname,PrimarykeyTablename,PrimarykeyColumnname,Hierarchy,level,rnk) 
    AS 
    ( 
        SELECT                   
                               soc.object_id 
                              ,scc.name 
                              ,soc.name 
                              ,convert(sysname,null) 
                              ,convert(int,null) 
                              ,convert(sysname,null) 
                              ,convert(sysname,null) 
                              ,convert(sysname,null) 
                              ,CONVERT(VARCHAR(MAX), scc.name + '.' + soc.name  ) as Hierarchy 
                              ,0 as level 
                              ,rnk=convert(varchar(max),soc.object_id) 
        FROM SYS.objects soc 
        JOIN sys.schemas scc 
          ON soc.schema_id = scc.schema_id 
       WHERE scc.name =@Schemaname 
         AND soc.name =@Tablename 
      UNION ALL 
      SELECT                   sop.object_id 
                              ,scp.name 
                              ,sop.name 
                              ,socp.name 
                              ,soc.object_id 
                              ,scc.name 
                              ,soc.name 
                              ,socc.name 
                              ,CONVERT(VARCHAR(MAX), f.Hierarchy + ' --> ' + scp.name + '.' + sop.name ) as Hierarchy 
                              ,f.level+1 as level 
                              ,rnk=f.rnk + '-' + convert(varchar(max),sop.object_id) 
        FROM SYS.foreign_key_columns sfc 
        JOIN Sys.Objects sop 
          ON sfc.parent_object_id = sop.object_id 
        JOIN SYS.columns socp 
          ON socp.object_id = sop.object_id 
         AND socp.column_id = sfc.parent_column_id 
        JOIN sys.schemas scp 
          ON sop.schema_id = scp.schema_id 
        JOIN SYS.objects soc 
          ON sfc.referenced_object_id = soc.object_id 
        JOIN SYS.columns socc 
          ON socc.object_id = soc.object_id 
         AND socc.column_id = sfc.referenced_column_id 
        JOIN sys.schemas scc 
          ON soc.schema_id = scc.schema_id 
        JOIN fkey f 
          ON f.ReferencingObjectid = sfc.referenced_object_id 
        WHERE ISNULL(f.PrimarykeyObjectid,0) <> f.ReferencingObjectid 
      ) 

     INSERT INTO @fkeytbl 
     (ReferencingObjectid,ReferencingSchemaname,ReferencingTablename,ReferencingColumnname 
            ,PrimarykeyObjectid,PrimarykeySchemaname,PrimarykeyTablename,PrimarykeyColumnname,Hierarchy,level,rnk) 
     SELECT ReferencingObjectid,ReferencingSchemaname,ReferencingTablename,ReferencingColumnname 
            ,PrimarykeyObjectid,PrimarykeySchemaname,PrimarykeyTablename,PrimarykeyColumnname,Hierarchy,level,rnk 
       FROM fkey 

        SELECT F.Relationshiptree 
         FROM 
        ( 
        SELECT DISTINCT Replicate('------',Level) + CASE LEVEL WHEN 0 THEN '' ELSE '>' END +  ReferencingSchemaname + '.' + ReferencingTablename 'Relationshiptree' 
               ,RNK 
          FROM @fkeytbl 
          ) F 
        ORDER BY F.rnk ASC 

------------------------------------------------------------------------------------------------------------------------------- 
-- Generate the Delete / Select script 
------------------------------------------------------------------------------------------------------------------------------- 

    DECLARE @Sql VARCHAR(MAX) 
    DECLARE @RnkSql VARCHAR(MAX) 

    DECLARE @Jointables TABLE 
    ( 
    ID INT IDENTITY 
    ,Object_id int 
    ) 

    DECLARE @ProcessTablename SYSNAME 
    DECLARE @ProcessSchemaName SYSNAME 

    DECLARE @JoinConditionSQL VARCHAR(MAX) 
    DECLARE @Rnk VARCHAR(MAX) 
    DECLARE @OldTablename SYSNAME 

    IF @GenerateDeleteScripts = 1 or @GenerateSelectScripts = 1  
    BEGIN 

          WHILE EXISTS ( SELECT 1 
                           FROM @fkeytbl 
                          WHERE Processed = 0 
                            AND level > 0 ) 
          BEGIN 

            SELECT @ProcessTablename = '' 
            SELECT @Sql                 = '' 
            SELECT @JoinConditionSQL = '' 
            SELECT @OldTablename     = '' 


            SELECT TOP 1 @ProcessTablename = ReferencingTablename 
                  ,@ProcessSchemaName  = ReferencingSchemaname 
                  ,@Rnk = RNK  
              FROM @fkeytbl 
             WHERE Processed = 0 
              AND level > 0  
             ORDER BY level DESC 


            SELECT @RnkSql ='SELECT ' + REPLACE (@rnk,'-',' UNION ALL SELECT ')  

            DELETE FROM @Jointables 

            INSERT INTO @Jointables 
            EXEC(@RnkSql) 

            IF @GenerateDeleteScripts = 1 
                SELECT @Sql = 'DELETE [' + @ProcessSchemaName + '].[' + @ProcessTablename + ']' + CHAR(10) + ' FROM [' + @ProcessSchemaName + '].[' + @ProcessTablename + ']' + CHAR(10) 

            IF @GenerateSelectScripts = 1 
                SELECT @Sql = 'SELECT  [' + @ProcessSchemaName + '].[' + @ProcessTablename + '].*' + CHAR(10) + ' FROM [' + @ProcessSchemaName + '].[' + @ProcessTablename + ']' + CHAR(10) 

            SELECT @JoinConditionSQL = @JoinConditionSQL  
                                           + CASE  
                                             WHEN @OldTablename <> f.PrimarykeyTablename THEN  'JOIN ['  + f.PrimarykeySchemaname  + '].[' + f.PrimarykeyTablename + '] ' + CHAR(10) + ' ON ' 
                                             ELSE ' AND '  
                                             END 
                                           + ' ['  + f.PrimarykeySchemaname  + '].[' + f.PrimarykeyTablename + '].[' + f.PrimarykeyColumnname + '] =  ['  + f.ReferencingSchemaname  + '].[' + f.ReferencingTablename + '].[' + f.ReferencingColumnname + ']' + CHAR(10)  
                     , @OldTablename = CASE  
                                         WHEN @OldTablename <> f.PrimarykeyTablename THEN  f.PrimarykeyTablename 
                                         ELSE @OldTablename 
                                         END 

                  FROM @fkeytbl f 
                  JOIN @Jointables j 
                    ON f.Referencingobjectid  = j.Object_id 
                 WHERE charindex(f.rnk + '-',@Rnk + '-') <> 0 
                   AND F.level > 0 
                 ORDER BY J.ID DESC 

            SELECT @Sql = @Sql +  @JoinConditionSQL 

            IF LTRIM(RTRIM(@WhereClause)) <> ''  
                SELECT @Sql = @Sql + ' WHERE (' + @WhereClause + ')' 

            PRINT @SQL 
            PRINT CHAR(10) 

            UPDATE @fkeytbl 
               SET Processed = 1 
             WHERE ReferencingTablename = @ProcessTablename 
               AND rnk = @Rnk 

          END 

          IF @GenerateDeleteScripts = 1 
            SELECT @Sql = 'DELETE FROM [' + @Schemaname + '].[' + @Tablename + ']' 

          IF @GenerateSelectScripts = 1 
            SELECT @Sql = 'SELECT * FROM [' + @Schemaname + '].[' + @Tablename + ']' 

          IF LTRIM(RTRIM(@WhereClause)) <> ''  
                SELECT @Sql = @Sql  + ' WHERE ' + @WhereClause 

         PRINT @SQL 
     END 

SET NOCOUNT OFF 


go 

My solution is based on @marc_s solution, i just concatenated columns in cases that a constraint is based on more than one column:我的解决方案基于@marc_s 解决方案,我只是在约束基于多个列的情况下连接列:

SELECT
   FK.[name] AS ForeignKeyConstraintName
  ,SCHEMA_NAME(FT.schema_id) + '.' + FT.[name] AS ForeignTable
  ,STUFF(ForeignColumns.ForeignColumns, 1, 2, '') AS ForeignColumns
  ,SCHEMA_NAME(RT.schema_id) + '.' + RT.[name] AS ReferencedTable
  ,STUFF(ReferencedColumns.ReferencedColumns, 1, 2, '') AS ReferencedColumns
FROM
  sys.foreign_keys FK
  INNER JOIN sys.tables FT
  ON FT.object_id = FK.parent_object_id
  INNER JOIN sys.tables RT
  ON RT.object_id = FK.referenced_object_id
  CROSS APPLY
  (
    SELECT
      ', ' + iFC.[name] AS [text()]
    FROM
      sys.foreign_key_columns iFKC
      INNER JOIN sys.columns iFC
      ON iFC.object_id = iFKC.parent_object_id
        AND iFC.column_id = iFKC.parent_column_id
    WHERE
      iFKC.constraint_object_id = FK.object_id
    ORDER BY
      iFC.[name]
    FOR XML PATH('')
  ) ForeignColumns (ForeignColumns)
  CROSS APPLY
  (
    SELECT
      ', ' + iRC.[name]AS [text()]
    FROM
      sys.foreign_key_columns iFKC
      INNER JOIN sys.columns iRC
      ON iRC.object_id = iFKC.referenced_object_id
        AND iRC.column_id = iFKC.referenced_column_id
    WHERE
      iFKC.constraint_object_id = FK.object_id
    ORDER BY
      iRC.[name]
    FOR XML PATH('')
  ) ReferencedColumns (ReferencedColumns)

Microsoft Visio is probably the best I've came across, although as far as I know it won't automatically generate based on your relationships. Microsoft Visio 可能是我遇到的最好的,尽管据我所知它不会根据您的关系自动生成。

EDIT: try this in Visio, could give you what you need http://office.microsoft.com/en-us/visio-help/reverse-engineering-an-existing-database-HA001182257.aspx编辑:在 Visio 中试试这个,可以给你你需要的东西http://office.microsoft.com/en-us/visio-help/reverse-engineering-an-existing-database-HA001182257.aspx

If you have LINQPad (it's free), this script I just wrote will list every possible path between every table in your database.如果您有LINQPad (它是免费的),我刚刚编写的这个脚本将列出数据库中每个表之间的所有可能路径。

Given the following database:给定以下数据库:

在此处输入图像描述

...the script will produce the following output: ...脚本将产生以下输出:

在此处输入图像描述

Or you can set the longestOnly flag at the top of the script, and it will just output the longest paths:或者你可以在脚本顶部设置longestOnly标志,它只会输出最长的路径:

在此处输入图像描述

And here's the script:这是脚本:

var longestOnly = true;

var pathLists = new List<List<string>>();

foreach (var table in Mapping.GetTables()) {
    var subPaths = new List<string>();
    pathLists.Add(subPaths);
    subPaths.Add(table.TableName);

    Go(table, subPaths);
}

var pathStrings = pathLists
    .Select(p => string.Join(", ", p))
    .Distinct()
    .OrderBy(p => p)
    .ToList();
    
if (longestOnly) {
    pathStrings.RemoveAll(z => pathStrings.Any(i => i != z && i.Contains(z)));
} else {
    pathStrings.RemoveAll(z => pathStrings.Any(i => i != z && i.StartsWith(z)));
}

pathStrings.Dump();


void Go(System.Data.Linq.Mapping.MetaTable table, List<string> paths)
{
    foreach (var association in table.RowType.Associations) {
        var subPaths = paths.Concat(new List<string>()).ToList(); // create a copy
        pathLists.Add(subPaths);
        var subPathTableName = association.OtherType.Table.TableName;

        if (!subPaths.Contains(subPathTableName)) {
            subPaths.Add(subPathTableName);
            var subPathTable = Mapping.GetTable(association.OtherMember.DeclaringType.Type);
            if (subPathTable != null) {
                Go(subPathTable, subPaths);
            }
        }
    }
}

For a complex database, this can take a surprisingly long time to complete, and will return a surprisingly large list of results.对于一个复杂的数据库,这可能需要非常长的时间才能完成,并且会返回一个非常大的结果列表。 I needed to write this for work, and the end result left me feeling pretty defeated.我需要为工作而写这个,最终结果让我感到非常失败。 :) :)

I couldn't find anything else that would do this, so I'm pretty happy with it, though.我找不到其他可以做到这一点的东西,所以我对此很满意。

All suggestions thus far have shown relationships between entities via primary and foreign keys.到目前为止,所有建议都通过主键和外键显示了实体之间的关系。 Occasionally, it may be useful to also identify relationships via dependencies.有时,通过依赖关系识别关系也可能很有用。 I found the need for this to identify the relationships between views and tables when building network graph visualizations.我发现在构建网络图可视化时需要识别视图和表之间的关系。

select distinct
      v.name as referencer_name,
      V.type_desc as referencer_type,
      o.name as referenced_entity_name,
      o.type_desc as referenced_entity_type
from sys.views v
join sys.sql_expression_dependencies d
    on d.referencing_id = v.object_id
    and d.referenced_id is not null
join sys.objects o
    on o.object_id = d.referenced_id
order by referencer_name;

The above code results in the following table:上面的代码结果如下表:

SQL 表结果截图

This can be further extended, using python, to generate network graphs to visually see linkages.这可以使用 python 进一步扩展,以生成网络图以直观地查看链接。

在此处输入图像描述

select * from information_schema.REFERENTIAL_CONSTRAINTS where 
UNIQUE_CONSTRAINT_SCHEMA = 'SCHEMA_NAME' 

This will list the constraints with SCHEMA_NAME enter image description here这将列出带有SCHEMA_NAME的约束在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM