【问题标题】:How to find out whether a table has some unique columns如何找出一个表是否有一些唯一的列
【发布时间】:2011-08-04 12:00:32
【问题描述】:

我使用 MS SQL Server。

我收到了一些没有约束的大桌子,没有钥匙,什么都没有。

我知道有些列具有唯一值。给定表是否有一种智能方法来查找具有唯一值的列?

现在我通过计算 DISTINCT 值是否与表中的行数一样多来手动为每一列执行此操作。

SELECT COUNT(DISTINCT col) FROM table

可以让 cusor 遍历所有列,但想知道是否有人知道更智能或内置的功能。

谢谢。

【问题讨论】:

    标签: sql sql-server unique


    【解决方案1】:

    这是一种与@JNK 基本相似的方法,但它不是打印计数,而是为每一列返回一个现成的答案,告诉您一列是否仅包含唯一值:

    DECLARE @table varchar(100), @sql varchar(max);
    SET @table = 'some table name';
    
    SELECT
      @sql = COALESCE(@sql + ', ', '') + ColumnExpression
    FROM (
      SELECT
        ColumnExpression =
          'CASE COUNT(DISTINCT ' + COLUMN_NAME + ') ' +
          'WHEN COUNT(*) THEN ''UNIQUE'' ' +
          'ELSE '''' ' +
          'END AS ' + COLUMN_NAME
      FROM INFORMATION_SCHEMA.COLUMNS
      WHERE TABLE_NAME = @table
    ) s
    
    SET @sql = 'SELECT ' + @sql + ' FROM ' + @table;
    PRINT @sql;  /* in case you want to have a look at the resulting query */
    EXEC(@sql);
    

    它只是将每一列的COUNT(DISTINCT column)COUNT(*) 进行比较。结果将是一个包含单行的表,其中每一列将包含值 UNIQUE 用于那些没有重复的列,如果存在重复则为空字符串。

    但上述解决方案仅适用于那些没有 NULL 的列。应该注意的是,当您要在列上创建唯一约束/索引时,SQL Server 不会忽略 NULL。如果一列只包含一个 NULL 并且所有其他值都是唯一的,您仍然可以在该列上创建唯一约束(但是,您不能将其设为主键,这需要值的唯一性和不存在 NULL)。

    因此,您可能需要对内容进行更彻底的分析,您可以使用以下脚本获得:

    DECLARE @table varchar(100), @sql varchar(max);
    SET @table = 'some table name';
    
    SELECT
      @sql = COALESCE(@sql + ', ', '') + ColumnExpression
    FROM (
      SELECT
        ColumnExpression =
          'CASE COUNT(DISTINCT ' + COLUMN_NAME + ') ' +
          'WHEN COUNT(*) THEN ''UNIQUE'' ' +
          'WHEN COUNT(*) - 1 THEN ' +
            'CASE COUNT(DISTINCT ' + COLUMN_NAME + ') ' +
            'WHEN COUNT(' + COLUMN_NAME + ') THEN ''UNIQUE WITH SINGLE NULL'' ' +
            'ELSE '''' ' +
            'END ' +
          'WHEN COUNT(' + COLUMN_NAME + ') THEN ''UNIQUE with NULLs'' ' +
          'ELSE '''' ' +
          'END AS ' + COLUMN_NAME
      FROM INFORMATION_SCHEMA.COLUMNS
      WHERE TABLE_NAME = @table
    ) s
    
    SET @sql = 'SELECT ' + @sql + ' FROM ' + @table;
    PRINT @sql;  /* in case you still want to have a look at the resulting query */
    EXEC(@sql);
    

    此解决方案通过检查三个值来考虑 NULL:COUNT(DISTINCT column)COUNT(column)COUNT(*)。它显示的结果与前一个解决方案类似,但列的可能诊断更加多样化:

    • UNIQUE 表示没有重复值和 NULL(可以是 PK 或具有唯一约束/索引);

    • UNIQUE WITH SINGLE NULL – 可以猜到,没有重复,但是有一个 NULL(不能是 PK,但可以有唯一的约束/索引);

    • UNIQUE with NULLs – 没有重复,两个或多个 NULL(如果您使用的是 SQL Server 2008,则可能只有非 NULL 值的条件唯一索引);

    • 空字符串 - 有重复,也可能是 NULL。

    【讨论】:

    • 给出错误“警告:空值被聚合或其他 SET 操作消除。”。不能从应用程序中使用。
    • @user998660:“来自对警告特别敏感的应用程序”,你的意思是?在那种情况下,不,它当然不能。不过,并非所有应用程序那么敏感。例如,我不记得在 Delphi 中遇到过产生此类警告的聚合问题。无论如何,我的印象是 OP 想要一个他们可以“手动”使用的解决方案,例如,在查询的情况下,在 SSMS 之类的工具中调用它。
    【解决方案2】:

    我认为这可能是最干净的方式。只需使用动态 sql 和单个 select 语句来创建一个查询,该查询为您提供总行数和每个字段的不同值的计数。

    在顶部填写数据库名称和表名。数据库名称部分非常重要,因为OBJECT_NAME 仅适用于当前数据库上下文。

    use DatabaseName
    
    DECLARE @Table varchar(100) = 'TableName'
    
    DECLARE @SQL Varchar(max)
    
    SET @SQL = 'SELECT COUNT(*) as ''Total'''
    
    SELECT @SQL = @SQL + ',COUNT(DISTINCT ' + name + ') as ''' + name + ''''
    FROM sys.columns c
    WHERE OBJECT_NAME(object_id) = @Table
    
    SET @SQL = @SQL + ' FROM ' + @Table
    
    exec @sql
    

    【讨论】:

    • 假设列中没有 NULL,这将非常有帮助。创建唯一约束/索引时,SQL Server 中会考虑 NULL。
    【解决方案3】:

    如果您使用的是 2008,则可以使用 SSIS 中的数据分析任务来返回每个表的候选键。

    这篇博文介绍了整个过程,非常简单:

    http://consultingblogs.emc.com/jamiethomson/archive/2008/03/04/ssis-data-profiling-task-part-8-candidate-key.aspx

    【讨论】:

      【解决方案4】:

      我的代码做了几句话:

      1. 读取所有表和列

      2. 创建一个临时表来保存具有重复键的表/列

      3. 对于每个表/列,它都会运行一个查询。如果它发现至少一个值的 count(*)>1 它会插入临时表

      4. 从系统表中选择的列和值与发现重复的表/列不匹配

        DECLARE @sql VARCHAR(max)
        DECLARE @table VARCHAR(100)
        DECLARE @column VARCHAR(100)
        
        
        CREATE TABLE #temp (tname VARCHAR(100),cname VARCHAR(100))
        
        DECLARE mycursor CURSOR FOR
        select t.name,c.name
        from sys.tables t
        join sys.columns c on t.object_id = c.object_id
        where system_type_id not in (34,35,99)
        
        OPEN mycursor
        FETCH NEXT FROM mycursor INTO @table,@column
        
        WHILE @@FETCH_STATUS = 0
        BEGIN
        SET @sql = 'INSERT INTO #temp SELECT DISTINCT '''+@table+''','''+@column+ ''' FROM ' + @table + ' GROUP BY ' + @column +' HAVING COUNT(*)>1 '
        EXEC (@sql)
        FETCH NEXT FROM mycursor INTO @table,@column
        END
        
        select t.name,c.name
        from sys.tables t
        join sys.columns c on t.object_id = c.object_id
        left join #temp on t.name = #temp.tname and c.name = #temp.cname
        where system_type_id not in (34,35,99) and #temp.tname IS NULL
        
        DROP TABLE #temp
        
        CLOSE mycursor
        DEALLOCATE mycursor
        

      【讨论】:

      • 感谢您的回答,我没有尝试过,因为它运行在所有表上,这不是我对这个特定问题所需要的。
      • 您可以在游标查询定义中放置一个附加子句:AND tname = your_table_name
      【解决方案5】:

      简单的一行代码怎么样:

      CREATE UNIQUE INDEX index_name ON table_name (column_name);
      

      如果创建了索引,那么您的 column_name 只有唯一值。如果您的 column_name 中有欺骗,您将收到一条错误消息。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-28
        • 2013-04-04
        • 1970-01-01
        • 2023-03-19
        相关资源
        最近更新 更多