【问题标题】:How do I introspect on a SQL Server?如何在 SQL Server 上进行自省?
【发布时间】:2011-02-19 13:50:28
【问题描述】:

我有一个带有供应商应用程序的服务器,该应用程序严重依赖数据库。我需要以自动化的方式对数据库中几个表中的数据进行一些小的更改。只是插入和更新,没什么特别的。供应商是供应商,我永远无法确定他们在升级期间何时更改数据库的架构。

为此,我如何以某种可编写脚本的方式询问 SQL 服务器,“嘿,这个表还存在吗?是的,很酷,好的,但是它有这个列吗?它的数据类型和大小是什么? ?它可以为空吗?你能给我一个表的列表吗?在这个表中,你能给我一个列的列表吗?那里有任何主键吗?我不需要为整个架构执行此操作,只需其中一部分,只需在启动之前快速检查数据库即可。

我们目前有 Microsoft SQL Server 2005,但它可能很容易迁移到 Microsoft SQL Server 2008。我在搜索时可能没有使用正确的术语。我知道 ORM 不仅是这种事情的开销太大,而且我没有机会向我的同事推销它。

【问题讨论】:

    标签: sql sql-server-2005 schema introspection


    【解决方案1】:

    运行如下所示的查询,从中您可以看到:

    • 架构名称
    • 表/视图名称
    • 表类型(如:SYSTEM_TABLE、VIEW、SQL_TABLE_VALUED_FUNCTION、USER_TABLE、SQL_INLINE_TABLE_VALUED_FUNCTION、INTERNAL_TABLE)
    • 列名
    • 列数据类型(包括长度、精度等)
    • 可空性
    • 此列在主键中的位置
    • 完整的主键,所有 PK 列连接在一起,如果此列是 PK 的一部分
    • 身份(种子、增量和当前值)
    • 检查约束定义
    • 计算列定义

    需要 SQL Server 2005+ 才能运行:

    --optional, remove comments on WHERE to use these
    --DECLARE @SchemaNameSearch   sysname
    --       ,@TableNameSearch    sysname
    --       ,@ColumnNameSearch   sysname
    --SELECT @SchemaNameSearch  ='YourSchemaName'
    --      ,@TableNameSearch   ='YourTableName'
    --      ,@ColumnNameSearch  ='YourColumnName'
    
    SELECT
        sh.name+'.'+o.name AS ObjectName
            ,o.type_desc AS ObjectType
            ,s.name as ColumnName
            ,CASE
                 WHEN t.name IN ('char','varchar') THEN t.name+'('+CASE WHEN s.max_length<0 then 'MAX' ELSE CONVERT(varchar(10),s.max_length) END+')'
                 WHEN t.name IN ('nvarchar','nchar') THEN t.name+'('+CASE WHEN s.max_length<0 then 'MAX' ELSE CONVERT(varchar(10),s.max_length/2) END+')'
                 WHEN t.name IN ('numeric') THEN t.name+'('+CONVERT(varchar(10),s.precision)+','+CONVERT(varchar(10),s.scale)+')'
                 ELSE t.name
             END AS DataType
    
            ,CASE
                 WHEN s.is_nullable=1 THEN 'NULL'
                ELSE 'NOT NULL'
            END AS Nullable
            ,xc.key_ordinal AS PK_Position
            ,CASE
                 WHEN xc.key_ordinal IS NOT NULL THEN All_PKs.PrimaryKey
                 ELSE NULL
             END AS PK
            ,CASE
                 WHEN ic.column_id IS NULL THEN ''
                 ELSE ' identity('+ISNULL(CONVERT(varchar(10),ic.seed_value),'')+','+ISNULL(CONVERT(varchar(10),ic.increment_value),'')+')='+ISNULL(CONVERT(varchar(10),ic.last_value),'null')
             END
            +CASE
                 WHEN sc.column_id IS NULL THEN ''
                 ELSE ' computed('+ISNULL(sc.definition,'')+')'
             END
            +CASE
                 WHEN cc.object_id IS NULL THEN ''
                 ELSE ' check('+ISNULL(cc.definition,'')+')'
             END
                AS MiscInfo
        FROM sys.objects                           o
            INNER JOIN sys.schemas                sh on o.schema_id=sh.schema_id
            INNER JOIN sys.columns                 s ON o.object_id=s.object_id
            INNER JOIN sys.types                   t ON s.system_type_id=t.system_type_id and t.is_user_defined=0
            LEFT OUTER JOIN sys.identity_columns  ic ON s.object_id=ic.object_id AND s.column_id=ic.column_id
            LEFT OUTER JOIN sys.computed_columns  sc ON s.object_id=sc.object_id AND s.column_id=sc.column_id
            LEFT OUTER JOIN sys.check_constraints cc ON s.object_id=cc.parent_object_id AND s.column_id=cc.parent_column_id
            LEFT OUTER JOIN sys.indexes            x ON o.object_id=x.object_id AND x.is_primary_key=1
            LEFT OUTER JOIN sys.index_columns     xc ON o.object_id=xc.object_id AND x.index_id=xc.index_id AND s.column_id=xc.column_id
            LEFT OUTER JOIN (SELECT --build the concatenated PK here
                                 oo.object_id
                                      ,STUFF(
                                                 (
                                                  SELECT 
                                                       ', '+s.Name
                                                      FROM sys.objects                           o
                                                          LEFT OUTER JOIN sys.indexes            x ON o.object_id=x.object_id AND x.is_primary_key=1
                                                          LEFT OUTER JOIN sys.index_columns     xc ON o.object_id=xc.object_id AND x.index_id=xc.index_id 
                                                          LEFT OUTER JOIN sys.columns            s ON o.object_id=s.object_id AND s.column_id=xc.column_id
                                                     WHERE oo.object_id=o.object_id AND xc.column_id IS NOT NULL
                                                     ORDER BY o.object_ID,xc.key_ordinal
                                                     FOR XML PATH('') 
                                                 )
                                                ,1,2, ''
                                            ) AS PrimaryKey
                                   FROM sys.objects  oo
                                   --
                                   --REMOVE comments to filter the query
                                   --WHERE oo.Name=@TableNameSearch 
                                   --
                            )All_PKs ON o.object_id=All_PKs.object_id
        --
        --REMOVE comments to filter the query
        --WHERE sh.name =@SchemaNameSearch
        --    AND o.Name=@TableNameSearch 
        --    AND s.name=@ColumnNameSearch
        --
        ORDER BY sh.name+'.'+o.name,s.column_id
    

    您可以删除 WHERE 上的注释以按架构/表/列进行过滤。

    您也可以只创建一个数据库触发器来提醒您发生变化:

    create this log table first:
    
    CREATE TABLE YourLogTable (EventID int not null identity(1,1), EventDateTime datetime null, EventDescription  varchar(MAX) null)
    
    USE [TheDatabase]
    GO
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    CREATE TRIGGER [YourDatabaseTrigger]
    ON DATABASE
    FOR DDL_DATABASE_LEVEL_EVENTS --DDL_TABLE_EVENTS    --DDL_EVENTS
    AS
    
    DECLARE @EventData      xml
    DECLARE @Message        varchar(1000)
    SET @EventData=EVENTDATA()
    
    INSERT INTO YourLogTable 
        (EventDateTime,EventDescription) 
        VALUES (GETDATE(),--SUSER_NAME()
                         --+'; '+@EventData.value('(/EVENT_INSTANCE/ObjectType)[1]', 'varchar(500)')
                         --+'; '+@EventData.value('(/EVENT_INSTANCE/ObjectName)[1]', 'varchar(500)')
                         --+'; '+@EventData.value('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]','nvarchar(8000)')
                         CONVERT(varchar(max),@EventData)
               )
    RETURN
    
    GO
    SET ANSI_NULLS OFF
    GO
    SET QUOTED_IDENTIFIER OFF
    GO
    ENABLE TRIGGER [YourDatabaseTrigger] ON DATABASE
    

    这将让您看到对数据库所做的每一次更改。

    【讨论】:

    • 这真的很棒。我没有使用你的代码,但它为我打开了很多大门,这比简单的习语和脚本要好。
    【解决方案2】:

    要通过 SQL 执行此操作,请使用 INFORMATION_SCHEMA 视图。

    要通过代码实现,请查看 SQL Server 管理对象 (SMO):

    http://msdn.microsoft.com/en-us/library/ms162169.aspx

    【讨论】:

      【解决方案3】:

      【讨论】:

      • 哇!对于这样一个通用链接,这是很多投票,甚至没有任何代码来做 OP 所追求的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-24
      • 2010-10-06
      • 1970-01-01
      • 1970-01-01
      • 2016-11-05
      • 2020-09-25
      相关资源
      最近更新 更多