从扩展属性中获取元数据 (SQL Server)

Posted

技术标签:

【中文标题】从扩展属性中获取元数据 (SQL Server)【英文标题】:Get metadata from extended properties (SQL Server) 【发布时间】:2021-03-17 16:21:22 【问题描述】:

我正在构建一个数据字典并尝试从扩展属性中获取一些信息。

我有一个添加扩展属性的脚本(按预期工作)。

EXEC sys.sp_addextendedproperty 
@name=N'MS_Description', 
@value=N'This column stores the bla bla bla.' ,
@level0type=N'SCHEMA', 
@level0name=N'dbo', --Schema Name
@level1type=N'TABLE', 
@level1name=N'my_super_crazy_table',--Table Name 
@level2type=N'COLUMN', 
@level2name=N'my_super_crazy_column'--Column Name
GO

现在我希望能够看到这些是何时创建/更新的(如果我能看到什么用户 -- suser_sname() 则奖励)


我对数据字典的最终查询如下:

SELECT tbl.name as [table_name],
       clmns.name as [column_name],
       exprop.value as [column_description]
       -- exprop.*
FROM sys.tables AS tbl
INNER JOIN sys.all_columns AS clmns ON clmns.object_id=tbl.object_id
LEFT OUTER JOIN sys.extended_properties exprop ON exprop.major_id = clmns.object_id AND exprop.minor_id = clmns.column_id
WHERE (tbl.name IN ('my_super_crazy_table') and exprop.class = 1) 

输出:

     table_name     |     column_name     |       column_description
my_super_crazy_table|my_super_crazy_column|This column stores the bla bla bla.

还有其他sys 表可以为我提供我正在寻找的信息吗? 谢谢

【问题讨论】:

看起来像是 DDL trigger 的工作。 您还需要什么其他信息?看起来你明白了,除了left join 后面跟着where 在它的一个列上的意义是什么?你的意思是on 而不是where 还是你的意思是inner join?# 【参考方案1】:

SQL Server 当前不存储任何关于何时添加或更新扩展属性或由谁更新的信息。

您可能最终想要实现更多的逻辑。这是我目前最好的脚本。

https://www.csvreader.com/posts/data_dictionary.sql

SELECT
    d.[primary key],
    d.[foreign key],
    CASE
        WHEN LEN(d.[column]) = 0 THEN d.[table]
        ELSE ''
    END AS [table],
    d.[column],
    CAST(d.[description] AS VARCHAR(MAX)) AS [description],
    d.[data type],
    d.nullable,
    d.[identity],
    d.[default]
FROM
    (
        SELECT
            '' AS [primary key],
            '' AS [foreign key],
            s.[name] AS [schema],
            CASE
                WHEN s.[name] = 'dbo' THEN t.[name]
                ELSE s.[name] + '.' + t.[name]
            END AS [table],
            '' AS [column],
            ISNULL(RTRIM(CAST(ep.[value] AS NVARCHAR(4000))), '') AS [description],
            '' AS [data type],
            '' AS nullable,
            '' AS [identity],
            '' AS [default],
            NULL AS column_id
        FROM
            sys.tables t

                INNER JOIN sys.schemas s ON
                    s.[schema_id] = t.[schema_id]

                -- get description of table, if available
                LEFT OUTER JOIN sys.extended_properties ep ON
                    ep.major_id = t.[object_id] AND
                    ep.minor_id = 0 AND
                    ep.name = 'MS_Description' AND
                    ep.class = 1
        WHERE
            t.is_ms_shipped = 0 AND
            NOT EXISTS
            (
                SELECT *
                FROM
                    sys.extended_properties ms
                WHERE
                    ms.major_id = t.[object_id] AND
                    ms.minor_id = 0 AND
                    ms.class = 1 AND
                    ms.[name] = 'microsoft_database_tools_support'
            )

        UNION ALL

        SELECT
            CASE
                WHEN pk.column_id IS NOT NULL THEN 'PK'
                ELSE ''
            END AS [primary key],
            CASE
                WHEN fk.primary_table IS NOT NULL
                    THEN fk.primary_table + '.' + fk.primary_column
                ELSE ''
            END AS [foreign key],
            s.[name] AS [schema],
            CASE
                WHEN s.[name] = 'dbo' THEN t.[name]
                ELSE s.[name] + '.' + t.[name]
            END AS [table],
            c.[name] AS [column],
            ISNULL(RTRIM(CAST(ep.[value] AS NVARCHAR(4000))), '') AS [description],
            CASE
                WHEN uty.[name] IS NOT NULL THEN uty.[name]
                ELSE ''
            END +
                CASE
                    WHEN uty.[name] IS NOT NULL AND sty.[name] IS NOT NULL THEN '('
                    ELSE ''
                END +
                CASE
                    WHEN sty.[name] IS NOT NULL THEN sty.[name]
                    ELSE ''
                END +
                CASE
                    WHEN sty.[name] IN ('char', 'nchar', 'varchar', 'nvarchar', 'binary', 'varbinary')
                        THEN '(' + 
                            CASE
                                WHEN c.max_length = -1 THEN 'max'
                                ELSE 
                                    CASE
                                        WHEN sty.[name] IN ('nchar', 'nvarchar')
                                            THEN CAST(c.max_length / 2 AS VARCHAR(MAX))
                                        ELSE
                                            CAST(c.max_length AS VARCHAR(MAX))
                                    END
                            END
                             + ')'
                    WHEN sty.[name] IN ('numeric', 'decimal')
                        THEN '(' + 
                            CAST(c.precision AS VARCHAR(MAX)) + ', ' + CAST(c.scale AS VARCHAR(MAX))
                             + ')'
                    ELSE
                        ''
                END +
                CASE
                    WHEN uty.[name] IS NOT NULL AND sty.[name] IS NOT NULL THEN ')'
                    ELSE ''
                END AS [data type],
            CASE
                WHEN c.is_nullable = 1 THEN 'Y'
                ELSE ''
            END AS nullable,
            CASE
                WHEN c.is_identity = 1 THEN 'Y'
                ELSE ''
            END AS [identity],
            ISNULL(dc.[definition], '') AS [default],
            c.column_id
        FROM
            sys.columns c
                INNER JOIN sys.tables t ON
                    t.[object_id] = c.[object_id]

                INNER JOIN sys.schemas s ON
                    s.[schema_id] = t.[schema_id]

                -- get name of user data type
                LEFT OUTER JOIN sys.types uty ON
                    uty.system_type_id = c.system_type_id AND
                    uty.user_type_id = c.user_type_id AND
                    c.user_type_id <> c.system_type_id

                -- get name of system data type
                LEFT OUTER JOIN sys.types sty ON
                    sty.system_type_id = c.system_type_id AND
                    sty.user_type_id = c.system_type_id

                -- get description of column, if available
                LEFT OUTER JOIN sys.extended_properties ep ON
                    ep.major_id = t.[object_id] AND
                    ep.minor_id = c.column_id AND
                    ep.[name] = 'MS_Description' AND
                    ep.[class] = 1

                -- get default's code text
                LEFT OUTER JOIN sys.default_constraints dc ON
                    dc.parent_object_id = t.[object_id] AND
                    dc.parent_column_id = c.column_id

                -- check for inclusion in primary key
                LEFT OUTER JOIN
                (
                    SELECT
                        ic.column_id,
                        i.[object_id]
                    FROM
                        sys.indexes i
                            INNER JOIN sys.index_columns ic ON
                                ic.index_id = i.index_id AND
                                ic.[object_id] = i.[object_id]
                    WHERE
                        i.is_primary_key = 1
                ) pk ON
                    pk.column_id = c.column_id AND
                    pk.[object_id] = t.[object_id]

                -- check for inclusion in foreign key
                LEFT OUTER JOIN
                (
                    SELECT
                        CASE
                            WHEN s.[name] = 'dbo' THEN pk.[name]
                            ELSE s.[name] + '.' + pk.[name]
                        END AS primary_table,
                        pkc.[name] as primary_column,
                        fkc.parent_object_id,
                        fkc.parent_column_id
                    FROM
                        sys.foreign_keys fk
                            INNER JOIN sys.tables pk ON
                                fk.referenced_object_id = pk.[object_id]
                            INNER JOIN sys.schemas s ON
                                s.[schema_id] = pk.[schema_id]
                            INNER JOIN sys.foreign_key_columns fkc ON
                                fkc.constraint_object_id = fk.[object_id] AND
                                fkc.referenced_object_id = pk.[object_id]
                            INNER JOIN sys.columns pkc ON
                                pkc.[object_id] = pk.[object_id] AND
                                pkc.column_id = fkc.referenced_column_id
                ) fk ON
                    fk.parent_object_id = t.[object_id] AND
                    fk.parent_column_id = c.column_id
        WHERE
            t.is_ms_shipped = 0 AND
            NOT EXISTS
            (
                SELECT *
                FROM
                    sys.extended_properties ms
                WHERE
                    ms.major_id = t.[object_id] AND
                    ms.minor_id = 0 AND
                    ms.class = 1 AND
                    ms.[name] = 'microsoft_database_tools_support'
            )
    ) d
ORDER BY
    d.[schema],
    d.[table],
    d.column_id;

【讨论】:

以上是关于从扩展属性中获取元数据 (SQL Server)的主要内容,如果未能解决你的问题,请参考以下文章

从 C# 检索 SQL Server 扩展属性

SQL Server 数据库中使用扩展属性的动态行级安全性

无法从我的 VS Code 扩展 (node.js) 连接到 SQL Server

如何摆脱 SQL Server 2000 中的扩展表属性?

需要使用java从sql查询中获取元数据

获取SQL Server中每个通道的Mirth使用的空间